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