之前在看 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é)果
非常好募书,一切都在我們的預(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é)果
似乎有些問(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輸出
也許你注意到了我說(shuō)的是大部分情況勾笆,我第一次運(yùn)行的時(shí)候出現(xiàn)了如下的log
PS:上圖內(nèi)容為第一次抓到的原始log和上面的代碼有一些細(xì)微不同
我們回過(guò)頭來(lái)看一下 IntentService 的 onDestroy()
代碼
@Override
public void onDestroy() {
mServiceLooper.quit();
}
注意到這里調(diào)用的是 Looper 的 quit()
方法桥滨,按照方法文檔的說(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ù)期