記一次IntentService的爬坑之旅

之前在看 IntentService 源碼的時(shí)候看到在 ServiceHandler 處理完消息之后會(huì)調(diào)用 stopSelf(startId) 結(jié)束當(dāng)前的Service叹坦,那么如果我們快速的調(diào)用兩次 startService 結(jié)果是如何呢熊镣,我們?cè)陉P(guān)鍵的地方加入日志

@Override
public void onCreate() {
    super.onCreate();
    Log.i(TAG, "onCreate");
}

@Override
public void onStart(Intent intent, int startId) {
    super.onStart(intent, startId);
    Log.i(TAG, "onStart startId:" + startId);
}

@Override
    public void onDestroy() {
    super.onDestroy();
    Log.i(TAG, "onDestroy");
}

@Override
protected void onHandleIntent(Intent intent) {
    Log.i(TAG, "onHandleIntent");
}

運(yùn)行,看一下log輸出結(jié)果

intent_service1.png

非常好募书,一切都在我們的預(yù)料之中绪囱,然而現(xiàn)實(shí)的工作不會(huì)那么簡(jiǎn)單,onHandlerIntent 中通常會(huì)執(zhí)行很多工作莹捡,我們?cè)?onHandlerIntent 中加入一些延時(shí)來(lái)模擬正常工作

@Override
protected void onHandleIntent(Intent intent) {
    try {
        Thread.currentThread().sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    Log.i(TAG, "thread name:" + Thread.currentThread().getName());
    Log.i(TAG, "onHandleIntent");
}

運(yùn)行鬼吵,看一下log輸出結(jié)果

intent_service2.png

似乎有些問(wèn)題,onDestroy() 并沒(méi)有執(zhí)行兩次篮赢,一定是哪里出了問(wèn)題齿椅。我們回想一下為什么會(huì)調(diào)用 onDestroy() ,是因?yàn)?ServiceHandler 在調(diào)用完 onHandleIntent之后又調(diào)用了 stopSelf(startId) 启泣,注意這里有個(gè)參數(shù) startId 涣脚,難道這里有什么貓膩?
我們看一下源碼的文檔

/**
 * Stop the service if the most recent time it was started was 
 * <var>startId</var>.  This is the same as calling {@link 
 * android.content.Context#stopService} for this particular service but allows you to 
 * safely avoid stopping if there is a start request from a client that you 
 * haven't yet seen in {@link #onStart}. 
 * 
 * <p><em>Be careful about ordering of your calls to this function.</em>.
 * If you call this function with the most-recently received ID before
 * you have called it for previously received IDs, the service will be
 * immediately stopped anyway.  If you may end up processing IDs out
 * of order (such as by dispatching them on separate threads), then you
 * are responsible for stopping them in the same order you received them.</p>
 * 
 * @param startId The most recent start identifier received in {@link 
 *                #onStart}.
 * @return Returns true if the startId matches the last start request
 * and the service will be stopped, else false.
 *  
 * @see #stopSelf()
 */

上面的文檔摘自 stopSelfResult(int startId) 兩個(gè)方法的區(qū)別在于有無(wú)返回值寥茫,文檔的大概意思就是通過(guò)最近生成的startId來(lái)結(jié)束Service遣蚀,如果startId不是最近的生成的纱耻,結(jié)束操作將會(huì)失敗芭梯,翻譯的比較搓膝迎,大概意思理解就行。這里就比較清楚了限次,因?yàn)榈谝淮谓Y(jié)束Service的時(shí)候使用的startId不是最新的(后面又生成了一次芒涡,因此第二次產(chǎn)生的startId才是最新的),因此結(jié)束操作失敗了费尽。這么簡(jiǎn)單的問(wèn)題當(dāng)然難不住機(jī)制的我們羊始,需要最新的startId旱幼,那我們直接傳遞最新的startId不就好了?我們將startId保存起來(lái)突委,結(jié)束的時(shí)候使用最新的startId柏卤,代碼如下

private int mStartId;

@Override
public void onStart(Intent intent, int startId) {
    super.onStart(intent, startId);
    Log.i(TAG, "onStart startId:" + startId );
    mStartId = startId;
}

@Override
protected void onHandleIntent(Intent intent) {
    try {
        Thread.currentThread().sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    Log.i(TAG, "thread name:" + Thread.currentThread().getName());
    Log.i(TAG, "onHandleIntent");
    stopSelf(mStartId);
}

運(yùn)行,大部分情況下我們會(huì)看到如下的log輸出

intent_service3.png

也許你注意到了我說(shuō)的是大部分情況勾笆,我第一次運(yùn)行的時(shí)候出現(xiàn)了如下的log

intent_service4.png

PS:上圖內(nèi)容為第一次抓到的原始log和上面的代碼有一些細(xì)微不同

我們回過(guò)頭來(lái)看一下 IntentServiceonDestroy() 代碼

@Override
public void onDestroy() {
    mServiceLooper.quit();
}

注意到這里調(diào)用的是 Looperquit() 方法桥滨,按照方法文檔的說(shuō)法是,quit方法會(huì)將所有的沒(méi)有分發(fā)的消息清空蒲每,而且MessageQueue的源碼也證實(shí)了這一點(diǎn)( quit最終會(huì)調(diào)用MessageQueue的removeAllMessagesLocked方法 )

private void removeAllMessagesLocked() {
    Message p = mMessages;
    while (p != null) {
        Message n = p.next;
        p.recycleUnchecked();
        p = n;
    }
    mMessages = null;
}

一般情況下如果出現(xiàn)了執(zhí)行結(jié)果多次不一致的現(xiàn)象喻括,我們可以考慮是多線程引起的異步問(wèn)題,那么為何會(huì)出現(xiàn)多線程異步問(wèn)題呢双妨?我們知道 onHandleIntent 是在IntentService中創(chuàng)建的子線程中執(zhí)行,而我們知道Service的生命周期方法,例如 onDestroy() 是在主線程中執(zhí)行的浩姥,這里就會(huì)出現(xiàn)執(zhí)行結(jié)果不一致的情況。我們?cè)?onDestroy() 方法中加一些延時(shí)

@Override
public void onDestroy() {
    try {
        Thread.currentThread().sleep(100);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    super.onDestroy();
    Log.i(TAG, "onDestroy " + Thread.currentThread().getName());
}

這時(shí)候我們發(fā)現(xiàn)log輸出基本如下兜挨,基本符合我們的預(yù)期

intent_service5.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拌汇,一起剝皮案震驚了整個(gè)濱河市弊决,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌与倡,老刑警劉巖昆稿,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異净响,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)馋贤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門掸掸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人扰付,你說(shuō)我怎么就攤上這事∈底颍” “怎么了盐固?”我有些...
    開(kāi)封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)志电。 經(jīng)常有香客問(wèn)我蛔趴,道長(zhǎng),這世上最難降的妖魔是什么孝情? 我笑而不...
    開(kāi)封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任箫荡,我火速辦了婚禮,結(jié)果婚禮上羔挡,老公的妹妹穿的比我還像新娘。我一直安慰自己睬魂,他們只是感情好镀赌,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著喉钢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪肠虽。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天闲延,我揣著相機(jī)與錄音韩玩,去河邊找鬼。 笑死合愈,一個(gè)胖子當(dāng)著我的面吹牛击狮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播彪蓬,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼档冬,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了捣郊?” 一聲冷哼從身側(cè)響起慈参,我...
    開(kāi)封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤驮配,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后壮锻,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡灰殴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年牺陶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片掰伸。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡狮鸭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出歧蕉,到底是詐尸還是另有隱情,我是刑警寧澤梳猪,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布蒸痹,位于F島的核電站,受9級(jí)特大地震影響匿沛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜逃呼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一者娱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧推姻,春花似錦框沟、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)厂捞。三九已至,卻和暖如春蔫敲,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背貌虾。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工裙犹, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人叶圃。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓掺冠,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親德崭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

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