在之前的一篇文章《基于場(chǎng)景解讀Android四大組件》中談到Service是Android提供給開發(fā)者的一個(gè)組件,主要用于后臺(tái)一些耗時(shí)任務(wù)的處理。其實(shí)Android系統(tǒng)中已經(jīng)存在了很多這樣在后臺(tái)執(zhí)行一些特定任務(wù)的系統(tǒng)級(jí)Service,比方說與我們開發(fā)中打交道最多的ActivityManager,WindowManager,PackageManager和InputManager等等。今天我們依然從具體使用場(chǎng)景來對(duì)Android中Service的具體功能進(jìn)行分析。
Service生命周期
從圖中可以看出Service的生命周期會(huì)根據(jù)啟動(dòng)方式的不同有不同的生命周期回調(diào)。其實(shí)startService和bindService的區(qū)別就是該service是否可以和啟動(dòng)它的組件(比如activity)通信,因?yàn)閎indService可以拿到Service的binder,binder就是用來實(shí)現(xiàn)IPC的嘛。下面我們具體分析下每個(gè)生命周期回調(diào):
onCreate
該接口是在Service實(shí)例被創(chuàng)建時(shí)調(diào)用,這里的Service實(shí)例跟Activity實(shí)例不一樣,我們知道Activity實(shí)例根據(jù)不同的啟動(dòng)模式可以有一個(gè)或者多個(gè)實(shí)例,但是,Service雖然也有兩種啟動(dòng)方式,在整個(gè)系統(tǒng)中卻只會(huì)有一個(gè)Service實(shí)例。為什么呢?換個(gè)角度看,這就好比PC端的C/S模式,使用一個(gè)服務(wù)端去處理多個(gè)客戶端的請(qǐng)求,這里就對(duì)應(yīng)一個(gè)Service去處理來自多個(gè)Activity的請(qǐng)求嘛,沒必要搞多個(gè),浪費(fèi)資源,而且你會(huì)發(fā)現(xiàn)系統(tǒng)級(jí)Service其實(shí)也都只有一個(gè)實(shí)例。那么在onCreate里面我們可以做些什么呢?當(dāng)然是初始化,比如創(chuàng)建數(shù)據(jù)緩存,線程池等等。Android系統(tǒng)給我們提供了一個(gè)IntentService,我們可以參考它的實(shí)現(xiàn)方式來做一些初始化操作,IntentService的onCreate源碼如下:
@Override
public void onCreate() {
// TODO: It would be nice to have an option to hold a partial wakelock
// during processing, and to have a static startService(Context, Intent)
// method that would launch the service & hand off a wakelock.
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
這里為什么要?jiǎng)?chuàng)建新的線程或者線程池呢?因?yàn)镾ervice默認(rèn)是在主線程中執(zhí)行的,所以我不建議你把一個(gè)后臺(tái)任務(wù)放在Service中的主線程執(zhí)行,因?yàn)槟菢泳褪チ薙ervice存在的初衷,還不如直接放在Activity里面做,除非你想要提升App進(jìn)程的優(yōu)先級(jí),防止App退到后臺(tái)被殺掉。
onStart
該接口是在調(diào)用startService方法時(shí)調(diào)用的,我們的后臺(tái)任務(wù)一般都會(huì)放在這里執(zhí)行,你可以通過intent獲取startService方法傳遞的參數(shù),這里依然以IntentService為例看下它的實(shí)現(xiàn)方式:
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
onBind
該方法是在調(diào)用bindService方法時(shí)調(diào)用,如果使用bindService方式來啟動(dòng)服務(wù)的話,一般發(fā)生在Activity需要與Service進(jìn)行通信的場(chǎng)景(比如說音樂播放器app里面就會(huì)用到),而Android的IPC主要是通過binder來實(shí)現(xiàn)的(也可以通過socket,在系統(tǒng)服務(wù)用的比較多),所以這里方法的返回值就需要一個(gè)binder實(shí)例。這里簡(jiǎn)單說下binder的實(shí)現(xiàn)機(jī)制(后續(xù)講Broadcast的時(shí)候我們?cè)诰唧w分析Android的IPC機(jī)制具體實(shí)現(xiàn)),其實(shí)就是一套PC上的C/S模式,用戶通過bindService接口獲取到Service的代理,然后通過這個(gè)代理來跟Service通信。我們這里用一段代碼來詳細(xì)說明下:
public class MyService extends Service {
Binder mService = new IMyAidlInterface.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
};
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return mService;
}
}
// call in activity
bindService(new Intent(this, MyService.class), new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mServiceProxy = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}, 0);
這里的代理分兩種:本地代理和遠(yuǎn)程代理,它們是按照service和activity是否在同一個(gè)進(jìn)程中來區(qū)分的(這里僅以activity和service通信為例來說明,其他場(chǎng)景原理類似),如果是在同一進(jìn)程,那么這個(gè)代理對(duì)象mServiceProxy其實(shí)就是mService對(duì)象。而當(dāng)它們不在同一個(gè)進(jìn)程中時(shí),mServiceProxy和mService就屬于不同進(jìn)程空間中的對(duì)象,由于不同進(jìn)程之間的數(shù)據(jù)不能直接訪問,所以這個(gè)時(shí)候binder driver就來充當(dāng)一個(gè)中間橋梁的作用,來完成參數(shù)和返回結(jié)果等數(shù)據(jù)的傳遞(其實(shí)也就是在Linux內(nèi)核空間開辟了一段共享內(nèi)存),從而實(shí)現(xiàn)通信的目的。當(dāng)然為了方便開發(fā)者使用binder,Android對(duì)binder的使用進(jìn)行了一定的封裝,提供了一個(gè)AIDL。通過AIDL我們就只需關(guān)心service提供的功能接口,而不用去關(guān)心這些接口調(diào)用的具體細(xì)節(jié)。所以從這里也可以看出,對(duì)于一個(gè)好的產(chǎn)品,不管它的用戶群是普通用戶還是程序員,使用的便捷性都是一個(gè)很重要的指標(biāo)。就好比現(xiàn)在市面上很多做SDK的,往往那些接口簡(jiǎn)單,文檔清晰的SDK,用的人也會(huì)多一些。
onRebind
該方法是在多次調(diào)用bindService和unbindService時(shí)會(huì)調(diào)用到該接口。該方法使用場(chǎng)景不多,一般我們不會(huì)在這里面做一些事情,不過可能會(huì)有一些數(shù)據(jù)統(tǒng)計(jì)放在這里以觀察用戶的某一操作行為。
onUnbind
該方法是在調(diào)用unbindService方法時(shí)調(diào)用,一般發(fā)生在activity中需要斷開與service的連接的場(chǎng)景。注意該接口有個(gè)返回值,默認(rèn)為false。如果你想要在onRebind里面做一些事情的話,那么這里需要返回true。
onDestroy
該方法會(huì)在Service銷毀時(shí)調(diào)用,一般在這里我們會(huì)釋放一些在onCreate中進(jìn)行初始化時(shí)所申請(qǐng)的資源,可以參考IntentService的實(shí)現(xiàn)方式:
@Override
public void onDestroy() {
mServiceLooper.quit();
}
一般可以通過stopService或者unbindService方式來銷毀不再需要的Service。而unbindService這種方式必須是沒有通過startService啟動(dòng)Service的情況,否則不會(huì)銷毀Service。
Service使用場(chǎng)景
為了滿足開發(fā)者處理后臺(tái)任務(wù)的需要,Android提供了Service這個(gè)組件,同時(shí)為了方便開發(fā)者使用Service,又封裝了一個(gè)IntentService。當(dāng)然,現(xiàn)在很多App在處理后臺(tái)任務(wù)的時(shí)候并沒有優(yōu)先使用Service,而是自己實(shí)現(xiàn)了一套線程池機(jī)制或者使用Android提供的AsyncTask來執(zhí)行后臺(tái)任務(wù),這里我們來分析下他們各自的優(yōu)劣:
- Service的優(yōu)點(diǎn)是系統(tǒng)原生支持,使用方便;創(chuàng)建進(jìn)程方便;可以提供給系統(tǒng)內(nèi)其他App使用;優(yōu)先級(jí)高,當(dāng)App退到后臺(tái)后不宜被殺死。缺點(diǎn)是由于啟動(dòng)Service涉及到多次IPC,運(yùn)行效率不高,而且受限于系統(tǒng)接口,使用不夠靈活。
- 線程池的優(yōu)點(diǎn)是運(yùn)行效率高,配置和使用靈活。缺點(diǎn)是多進(jìn)程實(shí)現(xiàn)不方便, 由于Android實(shí)現(xiàn)了一套進(jìn)程托管機(jī)制,我們不能直接創(chuàng)建一個(gè)新的進(jìn)程,而只能通過四大組件的形式創(chuàng)建新的進(jìn)程。
基于以上分析,我們可以看出,一般普通的異步任務(wù),比如網(wǎng)絡(luò)請(qǐng)求,數(shù)據(jù)庫(kù)或者文件相關(guān)操作,我們都會(huì)使用線程池的方式來做,因?yàn)檫@樣使用的系統(tǒng)開銷小,運(yùn)行效率高,而且隨著業(yè)務(wù)邏輯的復(fù)雜度增加,擴(kuò)展性也更強(qiáng)。然而,對(duì)于一些特殊場(chǎng)景,比如進(jìn)程?;睿褂玫谌絊DK服務(wù)比如地圖,IM等,就需要使用Service來實(shí)現(xiàn),因?yàn)檫@些服務(wù)一般與App主進(jìn)程隔離開,需要運(yùn)行在新進(jìn)程中以防止App主進(jìn)程發(fā)生異常崩潰時(shí),牽連第三方服務(wù)也掛掉。