一、Service 簡介
很多情況下,一些與用戶很少需要產(chǎn)生交互的應用程序,我們一般讓它們在后臺運行就行了,而且在它們運行期間我們?nèi)匀荒苓\行其他的應用。為了處理這種后臺進程,Android 引入了 Service 的概念。
#??? Service 在 Android 中是一種長生命周期的組件,它不實現(xiàn)任何用戶界面,是一個沒有界面的 Activity
#? ? Service 長期在后臺運行, 執(zhí)行不關乎界面的一些操作比如: 網(wǎng)易新聞服務,每隔 1分鐘去服務查看是否有最新新聞
#? ? Service 和 Thread 有點相似,但是使用 Thread 不安全, 不嚴謹
#? ? Service 和其他組件一樣,都是運行在主線程中,因此不能用它來做耗時的操作
二、Android 中的進程
1)Android 中進程的種類
進程優(yōu)先級由高到低,依次為:
? ? 1.Foreground process 前臺進程
? ? 2. Visible process 可視進程, 可以看見, 但不可以交互.
??? 3. Service process 服務進程
??? 4. Background process 后臺進程
??? 5. Empty process 空進程(當程序退出時, 進程沒有被銷毀, 而是變成了空進程)
2)進程的回收機制
??????? Android 系統(tǒng)有一套內(nèi)存回收機制,會根據(jù)優(yōu)先級進行回收。Android 系統(tǒng)會盡可能的維持程序的進程, 但是終究還是需要回收一些舊的進程節(jié)省內(nèi)存提供給新的或者重要的進程使用。
#? 進程的回收順序是:從低到高
#? 當系統(tǒng)內(nèi)存不夠用時, 會把空進程一個一個回收掉
# 當系統(tǒng)回收所有的完空進程不夠用時, 繼續(xù)向上回收后臺進程, 依次類推
#? 但是當回收服務, 可視, 前臺這三種進程時, 系統(tǒng)非必要情況下不會輕易回收, 如果需要回收掉這三種進程, 那么在系統(tǒng)內(nèi)存夠用時, 會再給重新啟動進程;但是服務進程如果用戶手動的關閉服務, 這時服務不會再重啟了。
3)為什么用服務而不是線程
????? 進程中運行著線程, Android 應用程序剛啟動都會開啟一個進程給這個程序來使用。Android 一個應用程序把所有的界面關閉時, 進程這時還沒有被銷毀, 現(xiàn)在處于的是空進程狀態(tài),Thread 運行在空進程中, 很容易的被銷毀了。
?????? 服務不容易被銷毀, 如果非法狀態(tài)下被銷毀了, 系統(tǒng)會在內(nèi)存夠用時, 重新啟動。
三、Service 的生命周期
service 的生命周期,從它被創(chuàng)建開始,到它被銷毀為止,可以有兩條不同的路徑。
被開啟的 service 通過其他組件調(diào)用 startService()被創(chuàng)建。這種 service 可以無限地運行下去,必須調(diào)用 stopSelf()方法或者其他組件調(diào)用 stopService()方法來停止它。當service 被停止時,系統(tǒng)會銷毀它。A bound service (綁定模式)被綁定的 service 是當其他組件(一個客戶)調(diào)用 bindService()來創(chuàng)建的。客戶可以通過一個 IBinder 接口和 service 進行通信??蛻艨梢酝ㄟ^ unbindService()方法來關閉這 種連接。一個 service 可以同時和多個客戶綁定,當多個客戶都解除綁定之后,系統(tǒng)會銷毀service。
Tips: Service 的這兩中生命周期并不是完全分開的。
也就是說,你可以和一個已經(jīng)調(diào)用了 startService()而被開啟的 service 進行綁定。比如,一個后臺音樂 service 可能因調(diào)用 startService()方法而被開啟了,稍后,可能用戶想要控制播放器或者得到一些當前歌曲的信息, 可以通過 bindService()將一個 activity 和 service 綁定。 這種情況下, stopService()或 stopSelf()實際上并不能停止這個 service,除非所有的客戶都解除綁定。
Service? 的生命周期回調(diào)函數(shù)
和 activity 一樣,service 也有一系列的生命周期回調(diào)函數(shù),你可以實現(xiàn)它們來監(jiān)測 service 狀態(tài)的變化,并且在適當?shù)臅r候執(zhí)行適當?shù)墓ぷ鳌?/p>
下面的 service 展示了每一個生命周期的方法:


這個圖說明了 service 典型的回調(diào)方法, 盡管這個圖中將開啟的 service 和綁定的service 分開,但是你需要記住,任何 service 都潛在地允許綁定。所以,一個被開啟的 service 仍然可能被綁定。實現(xiàn)這些方法,你可以看到兩層嵌套的 service 的生命周期。
積極活動的生命時間
service 積極活動的生命時間(active lifetime)是從 onStartCommand() 或onBind()被調(diào)用開始,它們各自處理由 startService()或 bindService()方法傳過來的 Intent 對象。
如果 service 是被開啟的,那么它的活動生命周期和整個生命周期一同結(jié)束。
如果 service 是被綁定的,它們它的活動生命周期是在 onUnbind()方法返回后結(jié)束。
Tips :盡管一個被開啟的 service 是通過調(diào)用 stopSelf() 或 stopService()來停止的,沒有一個對應的回調(diào)函數(shù)與之對應,即沒有 onStop()回調(diào)方法。所以,當調(diào)用了停止的方法,除非這個 service 和客戶組件綁定,否則系統(tǒng)將會直接銷毀它,onDestory()方法會被調(diào)用,并且是這個時候唯一會被調(diào)用的回調(diào)方法。
管理生命周期
當綁定 service 和所有客戶端解除綁定之后,Android 系統(tǒng)將會銷毀它, (除非它同時被 onStartCommand()方法開啟)。因此,如果你的 service 是一個純粹的綁定 service,那么你不需要管理它的生命周期。、
然而,如果你選擇實現(xiàn) onStartCommand()回調(diào)方法,那么你必須顯式地停止service,因為 service 此時被看做是開啟的。這種情況下,service 會一直運行到它自己調(diào)用 stopSelf()或另一個組件調(diào)用 stopService(),不論它是否和客戶端綁定。
另外,如果你的 service 被開啟并且接受綁定,那么當系統(tǒng)調(diào)用你的 onUnbind()方法時,如果想要在下次客戶端綁定時候接受一個 onRebind()的調(diào)用(而不是調(diào)用 onBind()),你可以選擇在 onUnbind()中返回 true。onRebind()的返回值為 void,但是客戶端仍然在onServiceConnected()回調(diào)方法中得到 IBinder 對象。
四、遠程服務調(diào)用實例
在 Android 平臺中,各個組件運行在自己的進程中,他們之間是不能相互訪問的,但是在程序之間是不可避免的要傳遞一些對象, 在進程之間相互通信。 ? ? 為了實現(xiàn)進程之間的相互通信, Android采用了一種輕量級的實現(xiàn)方式RPC(Remote Procedure Call 遠程進程調(diào)用)來完成進程之間的通信,并且 Android 通過接口定義語言(Android Interface DefinitionLanguage ,AIDL)來生成兩個進程之間相互訪問的代碼,例如,你在 Activity 里的代碼需要訪問 Service 中的一個方法,那么就可以通過這種方式來實現(xiàn)了。
AIDL 是 Android 的一種接口描述語言; 編譯器可以通過 aidl 文件生成一段代碼, 通過預先定義的接口達到兩個進程內(nèi)部通信進程的目的. 如果需要在一個 Activity 中, 訪問另一個Service 中的某個對象, 需要先將對象轉(zhuǎn)化成 AIDL 可識別的參數(shù)(可能是多個參數(shù)), 然后使用 AIDL 來傳遞這些參數(shù), 在消息的接收端, 使用這些參數(shù)組裝成自己需要的對象。
AIDL RPC 機制是通過接口來實現(xiàn)的,類似 Windows 中的 COM 或者 Corba,但他是輕量級的, 客戶端和被調(diào)用實現(xiàn)之間是通過代理模式實現(xiàn)的, 代理類和被代理類實現(xiàn)同一個接口 IBinder 接口。
下面是案例-商城支付的步驟:
需求:分別創(chuàng)建兩個工程,模擬一個支付平臺,暫且叫支付寶,模擬一個商戶端,叫商戶。商戶可以調(diào)用支付寶發(fā)布的遠程服務進行收款操作。
1)新創(chuàng)建一個 Android 工程《支付寶》。在 src 目錄下創(chuàng)建 com.share.alipay.aidl 包,然后在該包下創(chuàng)建 AlipayRemoteService.aidl 文件。在該文件中只聲明一個接口,在接口里聲明一個方法
package com.share.alipay.aidl;
interface AlipayRemoteService{????????????????????????????????????????????????????????????????????????????????????????????
?????????? boolean forwardPayMoney(float money);
}
Tips:當該 aidl 文件創(chuàng)建好以后 ADT 會自動在 gen 目錄下創(chuàng)建對應的類。
2)在《支付寶》src 目錄下創(chuàng)建 com.share.alipay.service 包,在該包中新建一個Service,叫 AlipayService,該類實現(xiàn)付款功能。代碼清單如下:

3)在《支付寶》工程的 AndroidManifest.xml 中注冊該 AlipayService。
4)創(chuàng)建一個新 Android 工程,名字叫《商戶》,包名:com.share.shop。使用默認的布局文件和默認的 MainActivity 類。
將《支付寶》工程中的 AlipayRemoteService.aidl 文件拷貝到《商戶》工程的 src 目錄下,同時注意添加對應的包名,要求包名必須跟該文件在原工程中的包名嚴格一致。
5)編輯 activity_main.xm 布局文件。
6)編寫 MainActivity 類,在該類中實現(xiàn)核心方法。


7)先將《支付寶》部署到模擬器,然后將《商戶》部署到模擬器,然后在《商戶》界面輸入一個金額, 然后點擊確定支付, 發(fā)現(xiàn) 《商戶》 工程已經(jīng)成功通過遠程服務調(diào)用了 《支付寶》中的服務。