從原始碼分析IntentService

weixin_34054866發表於2016-05-16

不管學什麼知識點最好從其基本用法開始,然後在深入到原始碼層學習會比較容易理解原始碼帶給我們的思想。所以我們們先來看看IntentService的基本用法。

其實很簡單,遵守一定的套路就可以

  1. 繼承IntentService並生成構造方法通過super()來完成父類的基本初始化操作
  2. 重寫父類的onHandleIntent方法並執行需要完成的後臺任務操作(上傳檔案等)
  3. 重寫onDestroy來完成些清理工作

比如:

public class MyIntentService extends IntentService {
    private static final String ACTION_BAZ = "com.example.comp.action.BAZ";

    private static final String EXTRA_PARAM1 = "com.example.comp.extra.PARAM1";
    private static final String EXTRA_PARAM2 = "com.example.comp.extra.PARAM2";

    public MyIntentService() {
        super("MyIntentService");
        setIntentRedelivery(false);
    }
    //用於啟動此服務的輔助方法
    public static void startActionBaz(Context context, String param1, String param2) {
        Intent intent = new Intent(context, MyIntentService.class);
        intent.setAction(ACTION_BAZ);
        intent.putExtra(EXTRA_PARAM1, param1);
        intent.putExtra(EXTRA_PARAM2, param2);
        context.startService(intent);
        Log.d("MyIntentService","startActionBaz");
        Log.d("MyIntentService","threadID:" + Thread.currentThread().getId());
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Log.d("MyIntentService","onHandleIntent:" + Thread.currentThread().getId());
        if (intent != null) {
            final String action = intent.getAction();
            if (ACTION_BAZ.equals(action)) {
                final String param1 = intent.getStringExtra(EXTRA_PARAM1);
                final String param2 = intent.getStringExtra(EXTRA_PARAM2);
                handleActionBaz(param1, param2);
            }
        }
    }

    private void handleActionBaz(String param1, String param2) {
        Log.d("MyIntentService","handleActionBaz:" + Thread.currentThread().getId());
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d("MyIntentService","onDestroy:" + Thread.currentThread().getId());
    }
}

在前臺activity中通過 MyIntentService.startActionBaz來啟動服務,大家可以注意log日誌的輸出檢視對應的方法是在哪個執行緒中執行的(通常主[ui]執行緒id為1,如果在對應的方法中輸出的執行緒id為1表明是在ui執行緒中執行的,那麼千萬就不要在這樣的方法中執行費時操作了,以避免ANR[程式未響應]的異常)。
輔助方法中是通過startService來啟動服務的,想必大家都知道在啟動成功後將呼叫Service的onCreate和onStartCommand方法,那 我們就可以從這兩個方法開始著手進入原始碼分析。

  • IntentService.java

    public void onCreate() {
        super.onCreate();
        HandlerThread thread = new   HandlerThread("IntentService[" + mName + "]");
      thread.start();
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
       }
    
      public int onStartCommand(Intent intent, int flags, int startId) {
          onStart(intent, startId);
          return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
      }
      public void onDestroy() {
          mServiceLooper.quit();//退出Looper訊息迴圈
      }
    

大概可以得出結論:

  1. 啟動Service時在onCreate方法中啟動了一個HandlerThread的執行緒並生成了一個Looper用來分發訊息之類的,同時建立了一個ServiceHandler的Handler用來處理訊息
  2. onStartCommand中呼叫了onStart方法,那麼在onStart方法中做了些什麼操作呢?
  • IntentService.java
    public void onStart(Intent intent, int startId) {
    Message msg = mServiceHandler.obtainMessage();
    msg.arg1 = startId;
    msg.obj = intent;
    mServiceHandler.sendMessage(msg);
    }
    很明顯就是通過ServiceHandler來傳送訊息(傳送到了HandlerThread的Looper訊息佇列中),訊息攜帶了intent物件(start Service啟動Service時的intent),那麼有傳送訊息就一定有處理訊息的程式碼。繼續檢視ServiceHandler的程式碼發現其是IntentService的內部類,定義如下:

      private final class ServiceHandler extends Handler {
          public ServiceHandler(Looper looper) {
              super(looper);
          }
    
          @Override
          public void handleMessage(Message msg) {
              onHandleIntent((Intent)msg.obj);
              stopSelf(msg.arg1);
          }
      }
    

可以看出在handleMessage中呼叫了onHandleIntent方法並在方法結束後stopSelf停止了Service。繼續檢視onHandleIntent方法的定義
IntentService.java
protected abstract void onHandleIntent(Intent intent);
OK,這個抽象方法必須在IntentService的子類中重寫,那麼在ServiceHandler的handleMessage中呼叫的將是子類中重寫的onHandleIntent方法,所以在我們的MyIntentService類中的onHandleIntent方法被呼叫(通過Java的多型機制實現了一個“模版方法”的設計模式)

到此原始碼基本分析結束,可以得出結論如下:

  1. 啟動IntentService型別的Service後,系統通過ServiceHandler將攜帶的Intent訊息放入由HandlerThread執行緒生成的Looper的訊息佇列中,Looper依次處理佇列中的訊息並通過dispatchMessage將訊息交給ServiceHandler的Handler來具體執行(其實就是Handler的用法,和我們在Activity中建立Handler並在handleMessage中更新ui的用法一樣,只不過這裡的handleMessage是在HandlerThread這樣的後臺執行緒而不是ui執行緒中執行的)
  2. 呼叫子類的onHandleIntent方法(用來執行費時操作),結束後關閉Service
    總之,這種機制通常用於希望按順序執行(序列)而非併發(並行)執行的費時操作,其中每個任務執行完畢的時間是未知的的應用場景。如果希望在任務結束後通知前臺可以通過sendBroadCast的方式傳送廣播。

HandlerThread類的關鍵程式碼(Service的onCreate方法中建立並start啟動了執行緒):

/**
典型的建立Looper步驟:
//生成Looper例項
Looper.prepare();
Looper.myLooper();
//啟動Looper迴圈
Looper.loop();
*/
public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
}
public Looper getLooper() {
    if (!isAlive()) {
        return null;
    }
    synchronized (this) {
        while (isAlive() && mLooper == null) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
    }
    return mLooper;
}

這裡比較難理解的是執行緒的安全同步程式碼,在onCreate方法中呼叫順序如下:

HandlerThread thread = new HandlerThread();
thread.start();
thread.getLooper();

想必大家都知道執行緒的基本知識,這裡就不多介紹了。線上程呼叫start後並不一定run方法立刻得到執行(非同步呼叫的),所以在執行thread.getLooper()時這個時候run還沒有執行。so,getLooper方法內部通過加鎖並有條件的wait()來一直等待mLooper不為空。在run方法中可以看到如果mLooper被初始化後會呼叫notifyAll()來通知所有正處於wait的執行緒繼續執行,這時getLooper方法中的return mLooper;將得到執行。

相關文章