Android多線程之IntentService源碼解析

想要了解 IntentService 的工作原理需要先對 Android 系統中以 Handler、Looper、MessageQueue 組成的異步消息處理機制以及 HandlerThread 有所瞭解,如果你還沒有這方面的知識,可以先看我寫的另外兩篇文章:

Android多線程之Handler、Looper與MessageQueue源碼解析

Android多線程之HandlerThread源碼解析

在介紹 Service 時我們經常會說 Service 適合於完成一些在後臺工作的任務,例如播放音樂、下載文件等,但 Service 默認是運行於 UI 線程的,如果想要依靠其來完成一些耗時任務,就需要自己來建立子線程,這相對比較繁瑣,所以官方也爲開發者提供了 IntentService 來解決這一問題

IntentService 有以下幾個特性:

  • IntentService 內部創建了一個工作線程,用於在子線程內執行傳遞給 onStartCommand() 的所有 Intent,開發者無須關心多線程問題
  • IntentService 內部通過 HandlerThread 和 Handler 來實現異步操作
  • IntentService 是以串行方式處理外部傳遞來的任務,即只有當上一個任務完成時,新的任務纔會被執行
  • 在處理完所有任務請求後會自動停止,因此不必手動調用 stopSelf() 方法
  • 提供了 onBind() 的默認實現(返回 null)
  • IntentService 是四大組件之一,擁有較高的優先級,不易被系統殺死,因此適合於執行一些高優先級的異步任務

IntentService 總的代碼不到一百行,如果你對 HandlerThread 有所瞭解,就可以很容易地瞭解 IntentService 的工作機制

先看下 IntentService 的類聲明

IntentService 類是一個抽象類,包含一個抽象方法需要由子類來實現

    public abstract class IntentService extends Service

包含的成員變量

    private volatile Looper mServiceLooper;

    //關聯了 mServiceLooper 的 Handler 
    private volatile ServiceHandler mServiceHandler;

    //IntentService 內部使用的子線程的線程名
    private String mName;

    //用於決定 onStartCommand() 方法的返回值
    private boolean mRedelivery;

構造函數

    //傳入子線程名
    public IntentService(String name) {
        super();
        mName = name;
    }

當 IntentService 第一次被啓動時,onCreate() 方法就會調用

就如同我們在 Activity 中使用 HandlerThread 來完成耗時任務一樣,在 IntentService 內部一樣是將 HandlerThread 作爲工作線程,因爲 mServiceHandler 持有了 HandlerThreadLooper 對象,因此 mServiceHandler 就作爲 IntentServiceHandlerThread 發送 Message 的橋樑

    @Override
    public void onCreate() {
        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        //觸發 HandlerThread 創建 Looper 對象
        thread.start();
        //獲取 Looper 對象,以此來構建可以向子線程發送 Message 的 Handler
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

這裏再看下 ServiceHandler 的類聲明

當中,消息隊列中的 Message 依次被傳遞給 handleMessage(Message) 方法進行處理,該方法是運行於子線程的,而耗時任務就交由抽象方法 onHandleIntent(Intent) 來完成,該方法需要交由子類來實現,並在該方法返回後調用 stopSelf(Int) 來停止 IntentService

可以看到,整個流程就是上面所說的

  1. IntentService 是以串行方式處理外部傳遞來的任務,即只有當上一個任務完成時,新的任務纔會被執行
  2. 在處理完所有任務請求後會自動停止,因此不必手動調用 stopSelf() 方法

但要注意的是,調用 stopSelf(Int)不一定會使 IntentService 停止,因爲消息隊列中可能還有未處理的消息,這個在後邊會介紹

    private final class ServiceHandler extends Handler {

        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            //將耗時任務轉交給 onHandleIntent() 方法來完成
            //onHandleIntent() 需要由子類來實現
            onHandleIntent((Intent)msg.obj);
            //msg.arg1 即 startId
            stopSelf(msg.arg1);
        }
    }

每次啓動 IntentService 時,onStartCommand() 方法就會被調用,最後是通過 onStart() 方法向 mServiceHandler 發送 包含此次任務詳情的 Message 對象

需要特別注意的是 startId 這個參數。每次回調 onStartCommand() 方法時,參數 startId 的值都是自動遞增的,startId 用於唯一標識每次對 IntentService 發起的處理請求。如果 IntentService 同時處理多個 onStartCommand() 請求,則不應在處理完一個啓動請求之後立即銷燬 IntentService 。因爲此時可能已經收到了新的啓動請求,在第一個請求結束時停止服務會導致第二個請求被終止。爲了避免這一問題,可以使用 stopSelf(int) 確保 IntentService 停止請求始終是基於最新一次的啓動請求。也就是說,如果調用 stopSelf(int) 方法的參數值與 onStartCommand() 接受到的最新的 startId 值不相符的話,stopSelf(int) 方法就會失效,從而避免終止尚未處理的請求

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

    //每次回調 onStartCommand() 方法時,參數 startId 的值都是自動遞增的,startId 用於唯一標識每次對 Service 發起的處理請求
    //如果 Service 同時處理多個 onStartCommand() 請求,則不應在處理完一個啓動請求之後立即銷燬 Service
    //因爲此時可能已經收到了新的啓動請求,在第一個請求結束時停止服務會導致第二個請求被終止
    //爲了避免這一問題,可以使用 stopSelf(int) 確保 Service 停止請求始終是基於最新一次的啓動請求
    //也就是說,如果調用 stopSelf(int) 方法的參數值與 onStartCommand() 接受到的最新的 startId 值不相符的話,stopSelf(int) 方法就會失效,從而避免終止尚未處理的請求
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

當中,onStartCommand() 方法的返回值可能由 setIntentRedelivery(boolean) 方法來決定

    //用於決定 onStartCommand() 方法的返回值
    //如果參數爲 true 則返回 START_REDELIVER_INTENT
    //如果系統在 onStartCommand() 返回後終止服務,則會重建服務,並通過傳遞給服務的最後一個 Intent 調用 onStartCommand()
    //任何掛起 Intent 均依次傳遞。這適用於主動執行應該立即恢復的作業(例如下載文件)的服務
    //如果參數爲 false 則返回 START_NOT_STICKY
    //如果系統在 onStartCommand() 返回後終止服務,則除非有掛起 Intent 要傳遞,否則系統不會重建服務
    //這是最安全的選項,可以避免在不必要時以及應用能夠輕鬆重啓所有未完成的作業時運行服務
    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }

還有其它幾個相關方法

    @Override
    public void onDestroy() {
        //移除所有待發送的 Message
        mServiceLooper.quit();
    }

    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        return null;
    }

    //此方法需要由子類來實現
    //在此方式中完成具體的耗時任務
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);

IntentService 的源碼理解起來不算難,不過這是建立在對 Android 系統中以 Handler、Looper、MessageQueue 組成的異步消息處理機制以及 HandlerThread 有所瞭解的基礎上的,最後就再貼上對 IntentService 的全部源碼註釋

package android.app;

import android.annotation.WorkerThread;
import android.annotation.Nullable;
import android.content.Intent;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;

/**
 * 作者:葉應是葉
 * 時間:2018/6/22 13:39
 * 描述:https://github.com/leavesC
 * https://www.jianshu.com/u/9df45b87cfdf
 */
public abstract class IntentService extends Service {

    private volatile Looper mServiceLooper;

    //關聯了 mServiceLooper 的 Handler
    private volatile ServiceHandler mServiceHandler;

    //IntentService 內部使用的子線程的線程名
    private String mName;

    //用於決定 onStartCommand() 方法的返回值
    private boolean mRedelivery;

    private final class ServiceHandler extends Handler {

        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            //將耗時任務轉交給 onHandleIntent() 方法來完成
            //onHandleIntent() 需要由子類來實現
            onHandleIntent((Intent)msg.obj);
            //msg.arg1 即 startId
            //如果當前
            stopSelf(msg.arg1);
        }
    }

    //傳入子線程名
    public IntentService(String name) {
        super();
        mName = name;
    }

    //用於決定 onStartCommand() 方法的返回值
    //如果參數爲 true 則返回 START_REDELIVER_INTENT
    //如果系統在 onStartCommand() 返回後終止服務,則會重建服務,並通過傳遞給服務的最後一個 Intent 調用 onStartCommand()
    //任何掛起 Intent 均依次傳遞。這適用於主動執行應該立即恢復的作業(例如下載文件)的服務
    //如果參數爲 false 則返回 START_NOT_STICKY
    //如果系統在 onStartCommand() 返回後終止服務,則除非有掛起 Intent 要傳遞,否則系統不會重建服務
    //這是最安全的選項,可以避免在不必要時以及應用能夠輕鬆重啓所有未完成的作業時運行服務
    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        //觸發 HandlerThread 創建 Looper 對象
        thread.start();
        //獲取 Looper 對象,以此來構建可以向子線程發送 Message 的 Handler
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

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

    //每次回調 onStartCommand() 方法時,參數 startId 的值都是自動遞增的,startId 用於唯一標識每次對 Service 發起的處理請求
    //如果 Service 同時處理多個 onStartCommand() 請求,則不應在處理完一個啓動請求之後立即銷燬 Service
    //因爲此時可能已經收到了新的啓動請求,在第一個請求結束時停止服務會導致第二個請求被終止
    //爲了避免這一問題,可以使用 stopSelf(int) 確保 Service 停止請求始終是基於最新一次的啓動請求
    //也就是說,如果調用 stopSelf(int) 方法的參數值與 onStartCommand() 接受到的最新的 startId 值不相符的話,stopSelf(int) 方法就會失效,從而避免終止尚未處理的請求
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        //移除所有待發送的 Message
        mServiceLooper.quit();
    }

    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        return null;
    }

    //此方法需要由子類來實現
    //在此方式中完成具體的耗時任務
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);

}

更多的源碼解讀請看這裏:Java_Android_Learn

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章