IntentService原理詳解

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é)到不少東西的懊亡。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市乎串,隨后出現(xiàn)的幾起案子斋配,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件艰争,死亡現(xiàn)場(chǎng)離奇詭異坏瞄,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)甩卓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門鸠匀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人逾柿,你說(shuō)我怎么就攤上這事缀棍。” “怎么了机错?”我有些...
    開封第一講書人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵爬范,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我弱匪,道長(zhǎng)青瀑,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任萧诫,我火速辦了婚禮斥难,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘帘饶。我一直安慰自己哑诊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開白布及刻。 她就那樣靜靜地躺著镀裤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪缴饭。 梳的紋絲不亂的頭發(fā)上淹禾,一...
    開封第一講書人閱讀 52,156評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音茴扁,去河邊找鬼铃岔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛峭火,可吹牛的內(nèi)容都是我干的毁习。 我是一名探鬼主播,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼卖丸,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼纺且!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起稍浆,我...
    開封第一講書人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤载碌,失蹤者是張志新(化名)和其女友劉穎猜嘱,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嫁艇,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡朗伶,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了步咪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片论皆。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖猾漫,靈堂內(nèi)的尸體忽然破棺而出点晴,到底是詐尸還是另有隱情,我是刑警寧澤悯周,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布粒督,位于F島的核電站,受9級(jí)特大地震影響禽翼,放射性物質(zhì)發(fā)生泄漏屠橄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一捐康、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧庸蔼,春花似錦解总、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至掏膏,卻和暖如春劳翰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背馒疹。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工佳簸, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人颖变。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓生均,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親腥刹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子马胧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容