從原始碼分析IntentService
不管學什麼知識點最好從其基本用法開始,然後在深入到原始碼層學習會比較容易理解原始碼帶給我們的思想。所以我們們先來看看IntentService的基本用法。
其實很簡單,遵守一定的套路就可以
- 繼承IntentService並生成構造方法通過super()來完成父類的基本初始化操作
- 重寫父類的onHandleIntent方法並執行需要完成的後臺任務操作(上傳檔案等)
- 重寫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訊息迴圈 }
大概可以得出結論:
- 啟動Service時在onCreate方法中啟動了一個HandlerThread的執行緒並生成了一個Looper用來分發訊息之類的,同時建立了一個ServiceHandler的Handler用來處理訊息
- 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的多型機制實現了一個“模版方法”的設計模式)
到此原始碼基本分析結束,可以得出結論如下:
- 啟動IntentService型別的Service後,系統通過ServiceHandler將攜帶的Intent訊息放入由HandlerThread執行緒生成的Looper的訊息佇列中,Looper依次處理佇列中的訊息並通過dispatchMessage將訊息交給ServiceHandler的Handler來具體執行(其實就是Handler的用法,和我們在Activity中建立Handler並在handleMessage中更新ui的用法一樣,只不過這裡的handleMessage是在HandlerThread這樣的後臺執行緒而不是ui執行緒中執行的)
- 呼叫子類的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;將得到執行。
相關文章
- Android中IntentService原始碼分析AndroidIntent原始碼
- IntentService原始碼Intent原始碼
- HandlerThread和IntentService原始碼解析threadIntent原始碼
- 從原始碼分析Axios原始碼iOS
- Android中IntentService的使用及其原始碼解析AndroidIntent原始碼
- 淺談IntentService原理分析Intent
- Android IntentService使用全面介紹及原始碼解析AndroidIntent原始碼
- NEO從原始碼分析看NEOVM原始碼
- 從面試角度分析ArrayList原始碼面試原始碼
- HashMap:從原始碼分析到面試題HashMap原始碼面試題
- HashMap從認識到原始碼分析HashMap原始碼
- 從原始碼角度分析 MyBatis 工作原理原始碼MyBatis
- 從原始碼分析Node的Cluster模組原始碼
- 從面試角度分析LinkedList原始碼面試原始碼
- RxJava2.x 從原始碼分析原理RxJava原始碼
- 從SpringMvc原始碼分析其工作原理SpringMVC原始碼
- 從原始碼分析Hystrix工作機制原始碼
- 從kratos分析BBR限流原始碼實現原始碼
- NEO從原始碼分析看數字資產原始碼
- 從原始碼分析 XtraBackup 的備份原理原始碼
- Retrofit原始碼分析三 原始碼分析原始碼
- NEO從原始碼分析看共識協議原始碼協議
- NEO從原始碼分析看網路通訊原始碼
- JUnit原始碼分析(四)——從Decorator模式說起原始碼模式
- 集合原始碼分析[2]-AbstractList 原始碼分析原始碼
- 集合原始碼分析[1]-Collection 原始碼分析原始碼
- 集合原始碼分析[3]-ArrayList 原始碼分析原始碼
- Guava 原始碼分析之 EventBus 原始碼分析Guava原始碼
- IntentServiceIntent
- 從 Masscan, Zmap 原始碼分析到開發實踐原始碼
- 從kratos分析breaker熔斷器原始碼實現原始碼
- Spring原始碼分析之AOP從解析到呼叫Spring原始碼
- 從原始碼分析RocketMq訊息的儲存原理原始碼MQ
- 透過原始碼分析RocketMQ主從複製原理原始碼MQ
- ButterKnife 從入門到精通 - 原始碼級分析(二)原始碼
- Android 原始碼分析之 AsyncTask 原始碼分析Android原始碼
- 【JDK原始碼分析系列】ArrayBlockingQueue原始碼分析JDK原始碼BloC
- 以太坊原始碼分析(36)ethdb原始碼分析原始碼