【Android】源碼分析 - IntentService機制

前言

提到Android的多線程機制,除了我們常用的Thread來實現異步任務之外,還有

  1. AsyncTask:封裝了線程池和Handler,主要爲了子線程更新UI;
  2. HandlerThread:一個已經擁有了Looper的線程類,內部可以直接使用Handler;
  3. IntentService:一個內部採用HandlerThread來執行任務的Service服務,任務執行完畢後會自動退出;

今天我們來根據平時的使用方式來分析一下第三個IntentSevice到底是什麼怎麼實現的?

使用IntentService

默認的 Service 是執行在主線程的,可是通常情況下,這很容易影響到程序的繪製性能(搶佔了主線程的資源)。除了 AsyncTask 與 HandlerThread,我們還可以選擇使用 IntentService 來實現異步操作。IntentService 繼承自普通 Service 同時又在內部創建了一個 HandlerThread,在 onHandlerIntent()的回調裏面處理扔到 IntentService 的任務。所以 IntentService 就不僅僅具備了異步線程的特性,還同時保留了 Service 不受主頁面生命週期影響的特點。

IntentService它本身是一個抽象類,因此使用它必須創建它的子類才能使用。所以這裏我們自定義一個MyIntentService,來處理異步任務:

public class MyIntentService extends IntentService {

    private static final String TAG = "MyIntentService";
    private boolean isRunning = true;
    private int count = 0;

    public MyIntentService() {
        super("MyIntentService");
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        try {
            //查看線程id
            Log.i(TAG, intent.getStringExtra("params") + ", 線程id:" + Thread.currentThread().getId());
            Thread.sleep(1000);

            //從0-100漸增
            isRunning = true;
            count = 0;
            while (isRunning) {
                count++;
                Log.i(TAG, "MyIntentService 線程運行中..." + count);
                if (count >= 100) {
                    isRunning = false;
                }
                Thread.sleep(50);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}

然後啓動服務之前別忘了在manifest文件中註冊這個Service:

// 在 Manifest 中註冊服務
<service android:name=".service.MyIntentService"/>

最後是啓動服務,就和普通Service一樣啓動:

// 像啓動 Service 那樣啓動 IntentService
Intent intent= new Intent(getActivity(), MyIntentService.class);
intent.putExtra("params", "testString...");
getActivity().startService(intent);

到此,通過IntentService執行的異步任務已經開始執行了,當執行完畢之後它會自動停止而不用我們手動操作。

當這個MyIntentService啓動之後,我們看到它接收到了消息並打印出了傳遞過去的intent參數,同時顯示onHandlerIntent方法執行的線程ID並非主線程,也就是說它果真開了一個額外的線程,什麼時候開啓的呢?我們進入IntentService源碼看看。

IntentService源碼

//IntentService繼承了Service並且它本身是一個抽象類,因此使用它必須創建它的子類才能使用。
public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;

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

        @Override
        public void handleMessage(Message msg) {
            // onHandleIntent 方法在工作線程中執行,執行完調用 stopSelf() 結束服務。
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }


    public IntentService(String name) {
        super();
        mName = name;
    }


    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }

    @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);
    }

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


    @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() {
        mServiceLooper.quit();
    }


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

    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}

代碼還是相當的簡潔的,首先通過定義我們可以知道IntentService是一個Service,並且是一個抽象類,所以我們在繼承IntentService的時候需要實現其抽象方法:onHandlerIntent。

1. 啓動 IntentService 爲什麼不需要新建線程?

我們來看看它的onCreate()函數:

private volatile ServiceHandler mServiceHandler;


@Override
public void onCreate() {
    super.onCreate();
    // HandlerThread 繼承自 Thread,內部封裝了 Looper,在這裏新建線程並啓動,所以啓動 IntentService 不需要新建線程。
    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
    thread.start();

    // 獲得工作線程的 Looper,並維護自己的消息隊列MessageQueue
    mServiceLooper = thread.getLooper();
    // mServiceHandler 是屬於這個工作線程的
    mServiceHandler = new ServiceHandler(mServiceLooper);
}

我們可以發現其內部定義一個HandlerThread(本質上是一個含有消息隊列的線程)。然後用成員變量維護其Looper和Handler,由於其Handler(也就是mServiceHandler對象)關聯着這個HandlerThread的Looper對象,所以這個ServiceHandler的handleMessage方法在HandlerThread線程中執行

然後我們發現其onStartCommand方法就是調用的其onStart方法,具體看一下其onStart方法:

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

很簡單就是將startId和啓動時接受到的intent對象傳遞到ServiceHandler的消息隊列中處理,那麼我們具體看一下ServiceHandler的處理邏輯:

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

    @Override
    public void handleMessage(Message msg) {
        // onHandleIntent 方法在工作線程中執行,執行完調用 stopSelf() 結束服務。
        onHandleIntent((Intent)msg.obj);
        stopSelf(msg.arg1);
    }
}

可以看到起handleMessage方法內部執行了兩個邏輯:

  • 一個是調用了其onHandlerIntent()抽象方法,在子線程中執行。

  • 二是調用了stopSelf()方法,這裏需要注意的是stopSelf方法傳遞了msg.arg1參數,從剛剛的onStart方法我們可以知道我們傳遞了startId,這是由於service可以啓動多次,可以傳遞N次消息,當IntentService的消息隊列中含有消息時調用stopSelf(startId)並不會立即stop自己,只有當消息隊列中最後一個消息被執行完成時纔會真正的stop自身

2. 爲什麼不建議通過 bindService() 啓動 IntentService?

我們看IntentService的onBind()方法:

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

IntentService 源碼中的 onBind() 默認返回 null,不適合 bindService() 啓動服務,如果你執意要 bindService() 來啓動 IntentService,可能因爲你想通過 Binder 或 Messenger 使得 IntentService 和 Activity 可以通信,這樣 onHandleIntent() 就不會被回調,相當於在你使用 Service 而不是 IntentService。

總結

IntentService 是繼承自 Service 並處理異步請求的一個抽象類,在 IntentService 內有一個工作線程來處理耗時操作,當任務執行完後,IntentService 會自動停止,不需要我們去手動結束。如果啓動 IntentService 多次,那麼每一個耗時操作會以工作隊列的方式在 IntentService 的 onHandleIntent 回調方法中執行,依次去執行,執行完自動結束。

IntentService有以下特點:

1). 它創建了一個獨立的工作線程來處理所有的通過onStartCommand()傳遞給服務的intents。
2). 創建了一個工作隊列,來逐個發送intent給onHandleIntent()。
3). 不需要主動調用stopSelft()來結束服務。因爲,在所有的intent被處理完後,系統會自動關閉服務。
4). 默認實現的onBind()返回null
5). 默認實現的onStartCommand()的目的是將intent插入到工作隊列中

繼承IntentService的類至少要實現兩個函數:構造函數onHandleIntent()函數。要覆蓋IntentService的其它函數時,注意要通過super調用父類的對應的函數。

不過,使用 IntentService 需要特別留意以下幾點:

  • 首先,因爲 IntentService 內置的是 HandlerThread 作爲異步線程,所以每一個交給 IntentService 的任務都將以隊列的方式逐個被執行到,一旦隊列中有某個任務執行時間過長,那麼就會導致後續的任務都會被延遲處理。

  • 其次,通常使用到 IntentService 的時候,我們會結合使用 BroadcastReceiver 把工作線程的任務執行結果返回給主 UI 線程。使用廣播容易引起性能問題,我們可以使用 LocalBroadcastManager 來發送只在程序內部傳遞的廣播,從而提升廣播的性能。我們也可以使用 runOnUiThread() 快速回調到主 UI 線程。

  • 最後,包含正在運行的 IntentService 的程序相比起純粹的後臺程序更不容易被系統殺死,該程序的優先級是介於前臺程序與純後臺程序之間的。

參考資料

發佈了103 篇原創文章 · 獲贊 255 · 訪問量 56萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章