Service使用場(chǎng)景解讀

在之前的一篇文章《基于場(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生命周期回調(diào)

從圖中可以看出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ù)也掛掉。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,333評(píng)論 25 708
  • 本文出自 Eddy Wiki ,轉(zhuǎn)載請(qǐng)注明出處:http://eddy.wiki/interview-androi...
    eddy_wiki閱讀 3,300評(píng)論 0 20
  • pastel intrusive seasoned
    Shaw233閱讀 168評(píng)論 0 0
  • 我今天晚上我在畫畫,我的夢(mèng)想是成為一個(gè)畫家,媽媽說,這周帶我我去一個(gè)有畫畫老師的地方,我喜歡各種動(dòng)物,晚上我畫了些...
    蘇暢寶閱讀 161評(píng)論 0 0
  • 前段時(shí)間在投資界風(fēng)靡全球的一段話: 紐約時(shí)間比加州時(shí)間早三個(gè)小時(shí),New York is 3 hours ahea...
    梓青彥閱讀 378評(píng)論 0 3