IntentService源碼閱讀
作者:jianzi
原文鏈接:IntentService源碼閱讀
更新時間:2016.11.29
Service作為Android四大組件之一,經(jīng)常會被用到,用于處理后臺事務枫攀。雖然Service經(jīng)常用于處理后臺事務,但是Service的生命周期回調函數(shù)都是在主線程中執(zhí)行的冯乘。
筆者在一開始接觸Service的時候,以為Service專門是用來執(zhí)行耗時任務的晒夹,因此在生命周期回調函數(shù)中做了很多耗時的操作裆馒。結果是顯而易見的姊氓,引發(fā)ANR了問題。Service的生命周期函數(shù)都是運行在主線程的喷好,因此耗時操作超過5s(不太確認)后之后就會出現(xiàn)ANR錯誤翔横。
為了解決ANR問題,常用的方式有Service生命周期中啟動一個子線程梗搅,在子線程中進行耗時操作禾唁。另外Android API提供了一個IntentService
,來處理比較耗時的后臺任務无切。
IntentService的用法和普通Service相同荡短,需要在AndroidManifest中進行注冊,并通過startService啟動哆键。
但是IntentService多了一個回調函數(shù)onHandleIntent(Intent intent)
掘托,集成了IntentService之后,并在這個函數(shù)中進行耗時操作即可籍嘹。IntentService在啟動之后會啟動一個工作線程烫映,而onHandleIntent函數(shù)就運行在這個工作線程中,因此再也不用擔心ANR問題了噩峦。
使用起來和普通Service一樣,調用startService抽兆,即可觸發(fā)onHandleIntent的執(zhí)行识补。
但是使用IntentService還需要注意幾個問題:
- IntentService需要一個帶有參數(shù)的構造函數(shù),但是Service需要一個不帶參數(shù)的默認構造函數(shù)辫红,因此再重載構造函數(shù)的時候凭涂,需要注意這一點。
- IntentService只會啟動一個工作線程贴妻,因此所有的onHandleIntent都會在這一個線程中串行的去執(zhí)行切油,同一時間只有一個任務被處理。
- 如果需要重寫onStartCommand方法名惩,一定要調用父類的實現(xiàn)澎胡。
下面我們來看看IntentService的源碼(加上注釋都沒超過200行代碼),他到底是如何實現(xiàn)異步處理的娩鹉,和普通Service+Thread有什么區(qū)別攻谁。
我們首先看看onCreate函數(shù):
@Override
public void onCreate() {
super.onCreate();
//創(chuàng)建一個工作線程
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
//獲取工作線程的Looper
mServiceLooper = thread.getLooper();
//創(chuàng)建一個Handler,用于在主線程中和工作線程通信
mServiceHandler = new ServiceHandler(mServiceLooper);
}
我們看到弯予,首先創(chuàng)建了一個HandlerThread
實例戚宦,我們后面會介紹他的實現(xiàn),現(xiàn)在只需要記住他是一個可以使用Handler的Thread就行了锈嫩。創(chuàng)建的這個線程用于處理各種耗時的后臺任務受楼,稱作工作線程垦搬。
同時還創(chuàng)建了一個ServiceHandler
實例,我們來看看它的實現(xiàn):
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
//執(zhí)行onHandleIntent艳汽,處理任務
onHandleIntent((Intent)msg.obj);
//執(zhí)行完之后猴贰,關閉Service
stopSelf(msg.arg1);
}
}
我們可以看到,ServiceHandler其實就是一個Handler的子類骚灸,而onHandleIntent就是在這個地方被調用的糟趾。我們知道Handler使用的哪個線程的Looper,handlerMessage就是在哪個線程執(zhí)行甚牲,因此onHandleIntent確實是在工作線程中執(zhí)行的义郑。而且同時也解釋了為什么所有的任務是串行的,因為只有一個工作線程丈钙,通過Looper來維持一個消息隊列非驮,來串行的執(zhí)行這些任務。
每次處理完任務之后雏赦,都會調用stopSelf來關閉服務劫笙,因此我們不需要在自己調用關閉服務。
現(xiàn)在我們可以猜測星岗,IntentService應該會在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中調用了onStart方法向工作線程發(fā)送了一條消息允华,并將Intent傳遞過去,從而觸發(fā)了onHandleIntent的執(zhí)行寥掐。
并且注釋中也介紹了靴寂,不建議在子類中重載onStartCommand方法,因為里面有消息的處理召耘。如果必須要重載的話百炬,需要調用父類的實現(xiàn),否則onHandleIntent是不會被觸發(fā)執(zhí)行的污它。
最后我們來看看onDestroy函數(shù):
Override
public void onDestroy() {
mServiceLooper.quit();
}
在這個函數(shù)中剖踊,使用在onCreate獲取的Looper實例衫贬,關閉整個消息隊列蜜宪。因此工作線程也會結束執(zhí)行,整個Service結束運行祥山。
最后,我們來總結一下:
- onCreate:啟動工作線程缝呕,并創(chuàng)建和工作線程通信的Handler澳窑;
- onStartCommand:通過Handler將消息轉發(fā)到工作線程中斧散,觸發(fā)onHandleIntent的執(zhí)行,處理耗時任務摊聋;
- onHandleIntent:調用者自己需要執(zhí)行的耗時的后臺任務鸡捐;
- onDestroy:關閉工作線程的消息隊列,退出工作線程麻裁,結束Service箍镜。
看完源碼之后,我們會發(fā)現(xiàn)使用IntentService和Service+Thread沒有本質上的區(qū)別煎源。但是個人還是建議色迂,如果有耗時的任務優(yōu)先使用IntentService進行處理。
因為IntentService內部只維持了一個工作線程手销,通過Handler來與其通信歇僧。比起一般的做法,處理一個任務創(chuàng)建就創(chuàng)建一個線程有消耗更小一點锋拖。而且IntentService畢竟是官方推薦的诈悍,接口使用起來十分簡單,不需要開發(fā)者自己去維護線程兽埃,節(jié)省了開發(fā)成本侥钳。
但是也并不是絕對,因為IntentService只有一個工作線程柄错,對于那種需要并發(fā)的任務來講舷夺,還是需要自己處理了。
好了鄙陡,就到這吧。