IntentService源碼閱讀

IntentService源碼閱讀

作者:jianzi

原文鏈接:IntentService源碼閱讀

更新時(shí)間:2016.11.29

Service作為Android四大組件之一,經(jīng)常會被用到,用于處理后臺事務(wù)。雖然Service經(jīng)常用于處理后臺事務(wù),但是Service的生命周期回調(diào)函數(shù)都是在主線程中執(zhí)行的。

筆者在一開始接觸Service的時(shí)候,以為Service專門是用來執(zhí)行耗時(shí)任務(wù)的,因此在生命周期回調(diào)函數(shù)中做了很多耗時(shí)的操作。結(jié)果是顯而易見的,引發(fā)ANR了問題。Service的生命周期函數(shù)都是運(yùn)行在主線程的,因此耗時(shí)操作超過5s(不太確認(rèn))后之后就會出現(xiàn)ANR錯(cuò)誤。

為了解決ANR問題,常用的方式有Service生命周期中啟動一個(gè)子線程,在子線程中進(jìn)行耗時(shí)操作。另外Android API提供了一個(gè)IntentService,來處理比較耗時(shí)的后臺任務(wù)。

IntentService的用法和普通Service相同,需要在AndroidManifest中進(jìn)行注冊,并通過startService啟動。

但是IntentService多了一個(gè)回調(diào)函數(shù)onHandleIntent(Intent intent),集成了IntentService之后,并在這個(gè)函數(shù)中進(jìn)行耗時(shí)操作即可。IntentService在啟動之后會啟動一個(gè)工作線程,而onHandleIntent函數(shù)就運(yùn)行在這個(gè)工作線程中,因此再也不用擔(dān)心ANR問題了。

使用起來和普通Service一樣,調(diào)用startService,即可觸發(fā)onHandleIntent的執(zhí)行。

但是使用IntentService還需要注意幾個(gè)問題:

  • IntentService需要一個(gè)帶有參數(shù)的構(gòu)造函數(shù),但是Service需要一個(gè)不帶參數(shù)的默認(rèn)構(gòu)造函數(shù),因此再重載構(gòu)造函數(shù)的時(shí)候,需要注意這一點(diǎn)。
  • IntentService只會啟動一個(gè)工作線程,因此所有的onHandleIntent都會在這一個(gè)線程中串行的去執(zhí)行,同一時(shí)間只有一個(gè)任務(wù)被處理。
  • 如果需要重寫onStartCommand方法,一定要調(diào)用父類的實(shí)現(xiàn)。

下面我們來看看IntentService的源碼(加上注釋都沒超過200行代碼),他到底是如何實(shí)現(xiàn)異步處理的,和普通Service+Thread有什么區(qū)別。

我們首先看看onCreate函數(shù):

@Override
public void onCreate() {
    super.onCreate();
    //創(chuàng)建一個(gè)工作線程
    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
    thread.start();

    //獲取工作線程的Looper
    mServiceLooper = thread.getLooper();
    //創(chuàng)建一個(gè)Handler,用于在主線程中和工作線程通信
    mServiceHandler = new ServiceHandler(mServiceLooper);
}

我們看到,首先創(chuàng)建了一個(gè)HandlerThread實(shí)例,我們后面會介紹他的實(shí)現(xiàn),現(xiàn)在只需要記住他是一個(gè)可以使用Handler的Thread就行了。創(chuàng)建的這個(gè)線程用于處理各種耗時(shí)的后臺任務(wù),稱作工作線程。

同時(shí)還創(chuàng)建了一個(gè)ServiceHandler實(shí)例,我們來看看它的實(shí)現(xiàn):

private final class ServiceHandler extends Handler {
    public ServiceHandler(Looper looper) {
        super(looper);
    }

    @Override
    public void handleMessage(Message msg) {
        //執(zhí)行onHandleIntent,處理任務(wù)
        onHandleIntent((Intent)msg.obj);
        //執(zhí)行完之后,關(guān)閉Service
        stopSelf(msg.arg1);
    }
}

我們可以看到,ServiceHandler其實(shí)就是一個(gè)Handler的子類,而onHandleIntent就是在這個(gè)地方被調(diào)用的。我們知道Handler使用的哪個(gè)線程的Looper,handlerMessage就是在哪個(gè)線程執(zhí)行,因此onHandleIntent確實(shí)是在工作線程中執(zhí)行的。而且同時(shí)也解釋了為什么所有的任務(wù)是串行的,因?yàn)橹挥幸粋€(gè)工作線程,通過Looper來維持一個(gè)消息隊(duì)列,來串行的執(zhí)行這些任務(wù)。

每次處理完任務(wù)之后,都會調(diào)用stopSelf來關(guān)閉服務(wù),因此我們不需要在自己調(diào)用關(guān)閉服務(wù)。

現(xiàn)在我們可以猜測,IntentService應(yīng)該會在onStartCommand中通過ServiceHandler來發(fā)送消息,觸發(fā)onHandleIntent的執(zhí)行。我們繼續(xù)看代碼:

@Override
public void onStart(Intent intent, int startId) {
    Message msg = mServiceHandler.obtainMessage();
    msg.arg1 = startId;
    msg.obj = intent;
    mServiceHandler.sendMessage(msg);
}

/**
 * You should not override this method for your IntentService. Instead,
 * override {@link #onHandleIntent}, which the system calls when the IntentService
 * receives a start request.
 * @see android.app.Service#onStartCommand
 */
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    onStart(intent, startId);
    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}

和我們猜測的一樣,在onStartCommand中調(diào)用了onStart方法向工作線程發(fā)送了一條消息,并將Intent傳遞過去,從而觸發(fā)了onHandleIntent的執(zhí)行。

并且注釋中也介紹了,不建議在子類中重載onStartCommand方法,因?yàn)槔锩嬗邢⒌奶幚怼H绻仨氁剌d的話,需要調(diào)用父類的實(shí)現(xiàn),否則onHandleIntent是不會被觸發(fā)執(zhí)行的。

最后我們來看看onDestroy函數(shù):

Override
public void onDestroy() {
    mServiceLooper.quit();
}

在這個(gè)函數(shù)中,使用在onCreate獲取的Looper實(shí)例,關(guān)閉整個(gè)消息隊(duì)列。因此工作線程也會結(jié)束執(zhí)行,整個(gè)Service結(jié)束運(yùn)行。

最后,我們來總結(jié)一下:

  • onCreate:啟動工作線程,并創(chuàng)建和工作線程通信的Handler;
  • onStartCommand:通過Handler將消息轉(zhuǎn)發(fā)到工作線程中,觸發(fā)onHandleIntent的執(zhí)行,處理耗時(shí)任務(wù);
  • onHandleIntent:調(diào)用者自己需要執(zhí)行的耗時(shí)的后臺任務(wù);
  • onDestroy:關(guān)閉工作線程的消息隊(duì)列,退出工作線程,結(jié)束Service。

看完源碼之后,我們會發(fā)現(xiàn)使用IntentService和Service+Thread沒有本質(zhì)上的區(qū)別。但是個(gè)人還是建議,如果有耗時(shí)的任務(wù)優(yōu)先使用IntentService進(jìn)行處理。

因?yàn)镮ntentService內(nèi)部只維持了一個(gè)工作線程,通過Handler來與其通信。比起一般的做法,處理一個(gè)任務(wù)創(chuàng)建就創(chuàng)建一個(gè)線程有消耗更小一點(diǎn)。而且IntentService畢竟是官方推薦的,接口使用起來十分簡單,不需要開發(fā)者自己去維護(hù)線程,節(jié)省了開發(fā)成本。

但是也并不是絕對,因?yàn)镮ntentService只有一個(gè)工作線程,對于那種需要并發(fā)的任務(wù)來講,還是需要自己處理了。

好了,就到這吧。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容