IntentService繼承于Service,它的特點(diǎn)是什么呢听怕?就跟龍叔小程序的特點(diǎn)一樣——“用完即走”。
用過(guò)IntentService的人都知道虑绵,使用非常簡(jiǎn)單尿瞭,根本不用自己去建立線程啊,維護(hù)線程通訊啊翅睛,甚至連最后資源的釋放也不用我們自己處理声搁,我們只需專心于業(yè)務(wù)實(shí)現(xiàn)即可,也就是編寫onHandleIntent(Intent intent)方法中的代碼捕发,具體使用這里就不多說(shuō)了疏旨,其實(shí)我們?cè)谥暗奈恼拢?a href="http://www.reibang.com/p/79605cc4a49e" target="_blank">定時(shí)任務(wù)之Alarm,已經(jīng)使用了IntentService扎酷,今天我們主要來(lái)分析一下IntentService原理檐涝。
分析原理,當(dāng)然是要看源碼的,我們一步步來(lái)看:
@Override
public void onCreate() {
// TODO: It would be nice to have an option to hold a partial wakelock
// during processing, and to have a static startService(Context, Intent)
// method that would launch the service & hand off a wakelock.
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
啟動(dòng)一個(gè)IntentService谁榜,當(dāng)然先進(jìn)onCreate方法啦拉岁,從上面源碼我們可以看到,第一步我們創(chuàng)建了一個(gè)HandlerThread(至于HandlerThread相關(guān)的說(shuō)明惰爬,可查看:Handler相關(guān)看這篇就夠了),然后啟動(dòng)線程惫企,獲取Looper撕瞧,然后將HandlerThread中的Looper與ServiceHandler進(jìn)行綁定,好啦狞尔,我們看看ServiceHandler的源碼:
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);
}
}
ServiceHandler是一個(gè)內(nèi)部類丛版,繼承于Handler,在handleMessage中我們看到了熟悉的方法:onHandleIntent(Intent intent)偏序,哦页畦,原來(lái)我們使用IntentService中最重要的覆蓋的onHandleIntent方法是在這里調(diào)用的,這里我們說(shuō)明幾點(diǎn):
1)因?yàn)镾erviceHandler是與HandlerThread中的Looper綁定的研儒,所以onHandleIntent方法是在子線程中執(zhí)行的豫缨。
2)我們注意到執(zhí)行完onHandleIntent后,調(diào)用了stopSelf(msg.arg1)端朵,這就是為什么IntentService能做到“用完即走”的原因了好芭,因?yàn)閳?zhí)行完任務(wù)后它會(huì)停掉自己的服務(wù)。
3)注意:這里自己停掉服務(wù)用的是stopSelf(int startId)方法冲呢,而不是stopSelf() 舍败,為什么呢?那么我們就得先說(shuō)一下這兩個(gè)API之間的區(qū)別了敬拓,首先我們看stopSelf() 的源碼:
public final void stopSelf() {
stopSelf(-1);
}
發(fā)現(xiàn)原來(lái)stopSelf()調(diào)用的是stopSelf(int startId)邻薯,只不過(guò)startId為-1而已。
說(shuō)到這個(gè)startId乘凸,它是什么呢厕诡?其實(shí)它就是service的一個(gè)生命周期:onStartCommand(@Nullable Intent intent, int flags, int startId)中最后的一個(gè)參數(shù)。
我們都知道翰意,當(dāng)我們多次調(diào)用startService來(lái)啟動(dòng)同一個(gè)service時(shí)木人,只有第一次會(huì)執(zhí)行onCreate,然后會(huì)多次調(diào)用onStartCommand冀偶,如果你去打印log的話醒第,你會(huì)發(fā)現(xiàn)盡管onCreate只執(zhí)行一次,但是每次的startId卻是不同的进鸠,且都大于0稠曼。
而stopSelf(int startId)中的startId與onStartCommand中的startId是一一對(duì)應(yīng)的關(guān)系,所以客年,當(dāng)我們調(diào)用stopSelf(int startId)時(shí)霞幅,系統(tǒng)會(huì)檢測(cè)是否還有其它的startId存在漠吻,有的話就不銷毀當(dāng)前service,沒(méi)有的話則銷毀司恳。
而如果我們調(diào)用的是stopSelf()途乃,那么無(wú)論是否還存在其它的startId,都會(huì)立即銷毀當(dāng)前service扔傅。
這就是stopSelf()和stopSelf(int startId)兩個(gè)方法的區(qū)別耍共!
我們回到之前的的問(wèn)題,為什么IntentService中自停服務(wù)用的是stopSelf(int startId)而不是stopSelf()呢猎塞?
從上面比較兩個(gè)方法的區(qū)別我們不能得出:這是為了提高IntentService的利用率试读,也就是說(shuō),如果在onHandleIntent方法執(zhí)行完畢前荠耽,又調(diào)用了startService啟動(dòng)了同一個(gè)IntentService钩骇,那么我們就沒(méi)必要銷毀當(dāng)前service了,直接繼續(xù)用當(dāng)前service對(duì)象執(zhí)行任務(wù)即可铝量,這樣有利于減少了對(duì)象的銷毀及創(chuàng)建倘屹。
另外:這里插一點(diǎn),因?yàn)镮ntentService中用的是一個(gè)HandlerThread慢叨,也就是單一的線程唐瀑,所以,用IntentService來(lái)執(zhí)行任務(wù)只能是串行依次進(jìn)行插爹。
下面哄辣,我們繼續(xù)分析源碼,我們剛剛說(shuō)到了onStartCommand赠尾,那我們也來(lái)看看它的源碼:
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
onStartCommand第一步調(diào)用的是onStart力穗,從onStart方法我們可以看出,其實(shí)就是用上面的handler發(fā)送了消息气嫁,從主線程切換到了子線程執(zhí)行任務(wù)当窗,這個(gè)沒(méi)什么好說(shuō)的。
然后我們看下面一行代碼寸宵,返回值: mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
查看了一下mRedelivery默認(rèn)值是false崖面,當(dāng)然提供了相應(yīng)的方法可進(jìn)行設(shè)置,下面我們先簡(jiǎn)單說(shuō)明下onStartCommand幾種返回值及區(qū)別:
1)START_STICKY:如果service進(jìn)程被kill掉梯影,保留service的狀態(tài)為開始狀態(tài)巫员,但不保留遞送的intent對(duì)象。隨后系統(tǒng)會(huì)嘗試重新創(chuàng)建service甲棍,由于服務(wù)狀態(tài)為開始狀態(tài)简识,所以創(chuàng)建服務(wù)后一定會(huì)調(diào)用onStartCommand(Intent,int,int)方法。如果在此期間沒(méi)有任何啟動(dòng)命令被傳遞到service,那么參數(shù)Intent將為null七扰。
2)START_NOT_STICKY:“非粘性的”奢赂。使用這個(gè)返回值時(shí),如果在執(zhí)行完onStartCommand后颈走,服務(wù)被異常kill掉膳灶,系統(tǒng)不會(huì)自動(dòng)重啟該服務(wù)
3)START_REDELIVER_INTENT:重傳Intent。使用這個(gè)返回值時(shí)立由,如果在執(zhí)行完onStartCommand后袖瞻,服務(wù)被異常kill掉,系統(tǒng)會(huì)自動(dòng)重啟該服務(wù)拆吆,并將Intent的值傳入。
4)START_STICKY_COMPATIBILITY:START_STICKY的兼容版本脂矫,但不保證服務(wù)被kill后一定能重啟枣耀。
從上面的介紹我們知道了,onStartCommand默認(rèn)返回了START_NOT_STICKY庭再,所以捞奕,這里注意了,如果你使用IntentService執(zhí)行的任務(wù)非常重要的話拄轻,建議通過(guò)設(shè)置setIntentRedelivery將mRedelivery設(shè)置為true颅围,這樣一來(lái)onStartCommand的返回就變成了START_REDELIVER_INTENT,有利于異常情況下服務(wù)的重啟及恢復(fù)恨搓。
最后院促,我來(lái)看看銷毀的方法:
@Override
public void onDestroy() {
mServiceLooper.quit();
}
前面我們?cè)?a href="http://www.reibang.com/p/ba12682fcbf8" target="_blank">Handler相關(guān)看這篇就夠了這篇文章中介紹HandlerThread就說(shuō)了,使用HandlerThread必須注意用完釋放Looper斧抱,這不常拓?IntentService在onDestroy生命周期中,幫我們進(jìn)行了Looper的釋放辉浦,所以我們得以“用完即走”弄抬,什么都不用處理,超級(jí)方便宪郊。
好啦掂恕,大概也就這些了,別看IntentService的源碼也就一百多行弛槐,其實(shí)細(xì)究起來(lái)還是能學(xué)到不少東西的懊亡。