天天看點

從源碼分析IntentService

不管學什麼知識點最好從其基本用法開始,然後在深入到源碼層學習會比較容易了解源碼帶給我們的思想。是以咱們先來看看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;将得到執行。