IntentService源碼閱讀
作者:jianzi
原文鏈接:IntentService源碼閱讀
更新時(shí)間:2016.11.29
Service作為Android四大組件之一,經(jīng)常會被用到,用于處理后臺事務(wù)。雖然Service經(jīng)常用于處理后臺事務(wù),但是Service的生命周期回調(diào)函數(shù)都是在主線程中執(zhí)行的。
筆者在一開始接觸Service的時(shí)候,以為Service專門是用來執(zhí)行耗時(shí)任務(wù)的,因此在生命周期回調(diào)函數(shù)中做了很多耗時(shí)的操作。結(jié)果是顯而易見的,引發(fā)ANR了問題。Service的生命周期函數(shù)都是運(yùn)行在主線程的,因此耗時(shí)操作超過5s(不太確認(rèn))后之后就會出現(xiàn)ANR錯(cuò)誤。
為了解決ANR問題,常用的方式有Service生命周期中啟動一個(gè)子線程,在子線程中進(jìn)行耗時(shí)操作。另外Android API提供了一個(gè)IntentService,來處理比較耗時(shí)的后臺任務(wù)。
IntentService的用法和普通Service相同,需要在AndroidManifest中進(jìn)行注冊,并通過startService啟動。
但是IntentService多了一個(gè)回調(diào)函數(shù)onHandleIntent(Intent intent),集成了IntentService之后,并在這個(gè)函數(shù)中進(jìn)行耗時(shí)操作即可。IntentService在啟動之后會啟動一個(gè)工作線程,而onHandleIntent函數(shù)就運(yùn)行在這個(gè)工作線程中,因此再也不用擔(dān)心ANR問題了。
使用起來和普通Service一樣,調(diào)用startService,即可觸發(fā)onHandleIntent的執(zhí)行。
但是使用IntentService還需要注意幾個(gè)問題:
- IntentService需要一個(gè)帶有參數(shù)的構(gòu)造函數(shù),但是Service需要一個(gè)不帶參數(shù)的默認(rèn)構(gòu)造函數(shù),因此再重載構(gòu)造函數(shù)的時(shí)候,需要注意這一點(diǎn)。
- IntentService只會啟動一個(gè)工作線程,因此所有的onHandleIntent都會在這一個(gè)線程中串行的去執(zhí)行,同一時(shí)間只有一個(gè)任務(wù)被處理。
- 如果需要重寫onStartCommand方法,一定要調(diào)用父類的實(shí)現(xiàn)。
下面我們來看看IntentService的源碼(加上注釋都沒超過200行代碼),他到底是如何實(shí)現(xiàn)異步處理的,和普通Service+Thread有什么區(qū)別。
我們首先看看onCreate函數(shù):
@Override
public void onCreate() {
super.onCreate();
//創(chuàng)建一個(gè)工作線程
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
//獲取工作線程的Looper
mServiceLooper = thread.getLooper();
//創(chuàng)建一個(gè)Handler,用于在主線程中和工作線程通信
mServiceHandler = new ServiceHandler(mServiceLooper);
}
我們看到,首先創(chuàng)建了一個(gè)HandlerThread實(shí)例,我們后面會介紹他的實(shí)現(xiàn),現(xiàn)在只需要記住他是一個(gè)可以使用Handler的Thread就行了。創(chuàng)建的這個(gè)線程用于處理各種耗時(shí)的后臺任務(wù),稱作工作線程。
同時(shí)還創(chuàng)建了一個(gè)ServiceHandler實(shí)例,我們來看看它的實(shí)現(xiàn):
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
//執(zhí)行onHandleIntent,處理任務(wù)
onHandleIntent((Intent)msg.obj);
//執(zhí)行完之后,關(guān)閉Service
stopSelf(msg.arg1);
}
}
我們可以看到,ServiceHandler其實(shí)就是一個(gè)Handler的子類,而onHandleIntent就是在這個(gè)地方被調(diào)用的。我們知道Handler使用的哪個(gè)線程的Looper,handlerMessage就是在哪個(gè)線程執(zhí)行,因此onHandleIntent確實(shí)是在工作線程中執(zhí)行的。而且同時(shí)也解釋了為什么所有的任務(wù)是串行的,因?yàn)橹挥幸粋€(gè)工作線程,通過Looper來維持一個(gè)消息隊(duì)列,來串行的執(zhí)行這些任務(wù)。
每次處理完任務(wù)之后,都會調(diào)用stopSelf來關(guān)閉服務(wù),因此我們不需要在自己調(diào)用關(guān)閉服務(wù)。
現(xiàn)在我們可以猜測,IntentService應(yīng)該會在onStartCommand中通過ServiceHandler來發(fā)送消息,觸發(fā)onHandleIntent的執(zhí)行。我們繼續(xù)看代碼:
@Override
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
/**
* You should not override this method for your IntentService. Instead,
* override {@link #onHandleIntent}, which the system calls when the IntentService
* receives a start request.
* @see android.app.Service#onStartCommand
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
和我們猜測的一樣,在onStartCommand中調(diào)用了onStart方法向工作線程發(fā)送了一條消息,并將Intent傳遞過去,從而觸發(fā)了onHandleIntent的執(zhí)行。
并且注釋中也介紹了,不建議在子類中重載onStartCommand方法,因?yàn)槔锩嬗邢⒌奶幚怼H绻仨氁剌d的話,需要調(diào)用父類的實(shí)現(xiàn),否則onHandleIntent是不會被觸發(fā)執(zhí)行的。
最后我們來看看onDestroy函數(shù):
Override
public void onDestroy() {
mServiceLooper.quit();
}
在這個(gè)函數(shù)中,使用在onCreate獲取的Looper實(shí)例,關(guān)閉整個(gè)消息隊(duì)列。因此工作線程也會結(jié)束執(zhí)行,整個(gè)Service結(jié)束運(yùn)行。
最后,我們來總結(jié)一下:
- onCreate:啟動工作線程,并創(chuàng)建和工作線程通信的Handler;
- onStartCommand:通過Handler將消息轉(zhuǎn)發(fā)到工作線程中,觸發(fā)onHandleIntent的執(zhí)行,處理耗時(shí)任務(wù);
- onHandleIntent:調(diào)用者自己需要執(zhí)行的耗時(shí)的后臺任務(wù);
- onDestroy:關(guān)閉工作線程的消息隊(duì)列,退出工作線程,結(jié)束Service。
看完源碼之后,我們會發(fā)現(xiàn)使用IntentService和Service+Thread沒有本質(zhì)上的區(qū)別。但是個(gè)人還是建議,如果有耗時(shí)的任務(wù)優(yōu)先使用IntentService進(jìn)行處理。
因?yàn)镮ntentService內(nèi)部只維持了一個(gè)工作線程,通過Handler來與其通信。比起一般的做法,處理一個(gè)任務(wù)創(chuàng)建就創(chuàng)建一個(gè)線程有消耗更小一點(diǎn)。而且IntentService畢竟是官方推薦的,接口使用起來十分簡單,不需要開發(fā)者自己去維護(hù)線程,節(jié)省了開發(fā)成本。
但是也并不是絕對,因?yàn)镮ntentService只有一個(gè)工作線程,對于那種需要并發(fā)的任務(wù)來講,還是需要自己處理了。
好了,就到這吧。