一细办、起因
- 很久以前接手的一個比較老的項目中瞪浸,是使用Activity的名字作為tag來標識網(wǎng)絡請求的漓糙。在Activity的onDestroy回調(diào)中根據(jù)這個標識取消所有的網(wǎng)絡請求潦匈。但是在部分頁面闹丐,出現(xiàn)了比較奇怪的問題:從Activity A打開Activity B横殴,然后finish掉Activity B回到Activity A,這時候再次打開Activity B卿拴,Activity B中的網(wǎng)絡請求會出現(xiàn)概率性無回調(diào)導致頁面加載失敗滥玷,且毫無規(guī)律可循。后經(jīng)過仔細排查巍棱,原來是第一次打開的Activity B實例onDestroy回調(diào)延時造成的惑畴。也就是說,由于第一次打開的Activity B實例的onDestroy回調(diào)延時航徙,恰好在第二次打開的Activity B實例啟動后正在請求網(wǎng)絡的時候被執(zhí)行如贷,取消了全部以Activity B名字作為tag的網(wǎng)絡請求,當然會導致第二次打開的Activity B實例中網(wǎng)絡請求無回調(diào)了到踏。(此處暫不討論這種取消網(wǎng)絡請求的不合理性)
- 在某個項目中杠袱,產(chǎn)品經(jīng)理要求實現(xiàn)應用內(nèi)浮窗,根據(jù)各種需求細節(jié)窝稿,最終敲定的技術方案為:使用WindowManager來加載一個自定義浮窗楣富,并根據(jù)Activity的生命周期回調(diào)來確定當前App是在前臺還是后臺。最先想到的自然是維護一個計數(shù)器伴榔,在Activity的onStart處將計數(shù)器+1纹蝴,然后再onStop處將計算器-1庄萎。然而實際情況中,經(jīng)常發(fā)現(xiàn)onStop延時調(diào)用塘安,且延時時間不確定糠涛,導致浮窗關閉與App切換前后臺時間不同步。最終只能改用onResume和onPause兼犯,外加一大堆冗余判斷來保證浮窗能正常顯示關閉忍捡。
- 很多時候由于項目開發(fā)周期緊,碰到問題沒時間深究切黔,為了趕工砸脊,只是采取替代方案快速解決/繞開了存在的問題。這段時間需求少了一點纬霞,開始對項目中的一些重點頁面做性能分析和優(yōu)化凌埂。當對一個復雜的列表頁和詳情頁進行檢查的時候,再一次發(fā)現(xiàn)了onStop和onDestroy回調(diào)延時的問題险领。簡單分析無果侨舆,加之又回想起那些被未知原因的bug支配的恐懼,于是下定決心绢陌,要趁著現(xiàn)在有空來深入挖掘下回調(diào)延時的背后究竟隱藏著哪些不為人知的秘密挨下。
二、原因/結(jié)論
Activity調(diào)用流程:打開Activity A -> 打開Activity B -> 關閉Activity B脐湾,回到Activity A
- 回調(diào)延時:
由于要關閉的Activity B或者將要打開的Activity A往主線程的MessageQueue中連續(xù)不斷的post了大量的msg臭笆,導致主線程一直在不斷的進行消息循環(huán)處理這些消息而沒有得到停歇。因此秤掌,ActivityThread中Idler.queueIdle方法沒有被調(diào)用的機會愁铺,App側(cè)也就不能向ActivityManagerService(AMS)發(fā)起IPC告知自己有空閑時間來處理AMS側(cè)的任務,也就是AMS向App側(cè)發(fā)起IPC來進行Activity B的銷毀的正常流程被阻斷了闻鉴。所以茵乱,finish Activity B后,onDestroy不會被及時回調(diào)孟岛。具體延時多久瓶竭,要看主線程中堆積的msg什么時候被處理完,也就是說要看主線程什么時候閑下來渠羞。 - 延時10s:
除正常流程外斤贰,Android系統(tǒng)另行安排了一套流程來保證即使正常流程被阻斷以后,Activity B還是能被銷毀次询。這個流程就是用來預防正常流暢中App不會閑下來處理AMS側(cè)的IPC任務而設計的(源碼注釋:Schedule an idle timeout in case the app doesn't do it for us)荧恍。流程具體為:在關閉Activity B返回Activity A的時候,當AMS側(cè)發(fā)起IPC通知App側(cè)的Activity A進行resume的時候屯吊,同時也會向AMS自己的主線程發(fā)送一個msg送巡,該msg延時10s后執(zhí)行摹菠。該msg的處理方為ActivityStackSupervisor#ActivityStackSupervisorHandler,msg的具體內(nèi)容與正常流程App段空閑時需要執(zhí)行的任務內(nèi)容一致授艰,當然也包括銷毀Activity B辨嗽。這樣世落,Activity B的onDestroy方法也就在延時10s后被調(diào)用了淮腾。
三、問題復現(xiàn)
由于實際項目中出現(xiàn)onStop和onDestroy回調(diào)延時的頁面代碼較為復雜屉佳,難以抽取展示谷朝。因而這里只給出一個簡單的示例來演示、復現(xiàn)問題武花。我們創(chuàng)建兩個Activity圆凰,分別為FirstActivity和SecondActivity,并在SecondActivity創(chuàng)建的時候開始往主線程不斷的post空msg体箕,如下:
// in SecondActivity.java
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
...
postMsg();
}
private void postMsg() {
view.post(new Runnable() {
@Override
public void run() {
try {
// 在主線程中休眠一小段時間
// 用來模擬主線程中諸如復雜的繪制专钉、復雜數(shù)據(jù)處理、幀動畫等等操作
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
view.postDelayed(new Runnable() {
@Override
public void run() {
postMsg();
}
}, 10);
}
}
代碼非常簡單累铅。我們知道跃须,view.post()方法底層就是往主線程的消息隊列MessageQueue發(fā)送消息(這里這么寫只是為了少寫兩行代碼),因為整段代碼邏輯就是娃兽,在SecondActivity中不斷向主線程post空msg菇民,來模擬復雜頁面中不停的重繪、數(shù)據(jù)流處理投储、幀動畫處理等第练。
測試流程:我們首先打開FirstActivity,然后在FirstActivity中通過StartActivity方法來打開SecondActivity玛荞,接著我們按下回退鍵娇掏,關閉SecondActivity回到FirstActivity。如果我們打印出FirstActivity和SecondActivity的生命周期回調(diào)勋眯,就不難發(fā)現(xiàn)婴梧,按下回退鍵后,在FirstActivity的onResume方法被調(diào)用后凡恍,SecondActivity的onStop和onDestroy方法并沒有緊接著被回調(diào)志秃,而是恰好在10s后才被回調(diào)。
四嚼酝、源碼探究
既然出現(xiàn)了問題浮还,當然要想辦法弄清楚才行。于是我們熟練的打開百度闽巩,輸入onDestroy回調(diào)延時钧舌,隨便找兩篇博客看看也就知道答案了担汤。
本文分析到此為止。
開個玩笑洼冻,要是常規(guī)操作能解決問題崭歧,我當然也就不用多此一舉來寫這么一篇問題分析的博文了。這里隨便貼幾篇百度搜到的博文:
- Activity中OnDestory()回調(diào)慢的原因及解決辦法
- Activity銷毀延遲10s執(zhí)行回調(diào)的踩坑之路
- Android Activity的onStop()與onDestroy() 回調(diào)緩慢撞牢,延時調(diào)用的問題解決方案
- ...
看完很絕望有沒有啊率碾。雖然很多博文都寫著原因分析,但完全沒有普適性屋彪。給出的解決方案也是要么繞開這個坑所宰,要么明著告訴你不要在onDestroy回調(diào)中去做balabala...。既然百度不行畜挥,那咱Google一下吧...Google好像也不太好使(或者說咱用的不好?)...還是不行仔粥,去stackoverflow看看,鑒于咱這英文閱讀速度和搜出來的質(zhì)量蟹但,在換了幾個關鍵詞之后也就放棄了躯泰。得,還是自己動手华糖,豐衣足食吧麦向。
首先從內(nèi)存泄漏的角度考察了一番,什么LeakCanary缅阳、Android Profile和大殺器MAT磕蛇,通通祭出來。結(jié)果鬧了半天十办,仍然沒有什么重大發(fā)現(xiàn)秀撇。看來是個棘手得問題向族,真叫人頭禿啊呵燕,差點泄氣要放棄了〖啵可是留下這個問題再扭,又讓人恨得牙癢癢夜矗,甚至就此留下陰影,成為以后職業(yè)生涯修煉時走火入魔的導火索紊撕。哎,既然捷徑都走不通区赵,那看來只能笨辦法,源碼分析走起漱受,我倒要看看系統(tǒng)這個鬼究竟搞了什么騷操作骡送。
我們就以Activity的finish方法作為入口,一步一步各谚,深入分析其流程紧憾。
本次分析到千,源碼基于Android 9.0昌渤。其中會有基于Binder的進程間通信和基于Handler的進程內(nèi)線程間通信相關知識,因為不是分析重點憔四,一筆帶過膀息。另外onStop方法延遲調(diào)用與onDestroy方法延遲調(diào)用的原因有關聯(lián),分析流程也基本一致了赵,所以這里以分析onDestroy方法延遲為主線潜支。
// in Activity.java
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback, WindowControllerCallback,
AutofillManager.AutofillClient {
public void finish() {
finish(DONT_FINISH_TASK_WITH_ACTIVITY);
}
private void finish(int finishTask) {
if (mParent == null) {
...
try {
if (resultData != null) {
resultData.prepareToLeaveProcess(this);
}
if (ActivityManager.getService()
.finishActivity(mToken, resultCode, resultData, finishTask)) {
mFinished = true;
}
} catch (RemoteException e) {
// Empty
}
...
}
}
}
這個流程還是比較簡單的,finish方法內(nèi)部調(diào)用了一個帶參數(shù)的私有finish方法柿汛,參數(shù)DONT_FINISH_TASK_WITH_ACTIVITY
從字面意思也可以看出來冗酿,就是只關閉Activity而不關閉Activity所在的任務棧。帶參數(shù)的finish方法中調(diào)用了ActivityManager.getService().finishActivity(mToken, resultCode, resultData, finishTask)
络断,通過IPC向AMS發(fā)起調(diào)用關閉當前Activity裁替。
// in ActivityManagerService
public class ActivityManagerService extends IActivityManager.Stub
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
public final boolean finishActivity(IBinder token, int resultCode, Intent resultData,
int finishTask) {
...
try {
boolean res;
final boolean finishWithRootActivity =
finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY;
if (finishTask == Activity.FINISH_TASK_WITH_ACTIVITY
|| (finishWithRootActivity && r == rootR)) {
res = mStackSupervisor.removeTaskByIdLocked(tr.taskId, false,
finishWithRootActivity, "finish-activity");
if (!res) {
Slog.i(TAG, "Removing task failed to finish activity");
}
} else {
// 由于finishtask標志為DONT_FINISH_TASK_WITH_ACTIVITY
// 因此這里進入else分支
res = tr.getStack().requestFinishActivityLocked(token, resultCode,
resultData, "app-request", true);
if (!res) {
Slog.i(TAG, "Failed to finish by app-request");
}
}
return res;
} finally {
Binder.restoreCallingIdentity(origId);
}
...
}
}
// in ActivityStack.java
class ActivityStack<T extends StackWindowController> extends ConfigurationContainer
implements StackWindowListener {
final boolean requestFinishActivityLocked(IBinder token, int resultCode,
Intent resultData, String reason, boolean oomAdj) {
...
finishActivityLocked(r, resultCode, resultData, reason, oomAdj);
...
}
final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData,
String reason, boolean oomAdj) {
//PAUSE_IMMEDIATELY定義在ActivityStackSupervisor中
//static final boolean PAUSE_IMMEDIATELY = true;
//即不要立即執(zhí)行暫停該Activity后的流程,而是延時500ms后再進行暫停Activity后的相關工作
//延時500ms其實也是一種保險機制貌笨,確保后續(xù)流程一定會被執(zhí)行弱判。正常的話App側(cè)在onPause后會發(fā)起IPC來告知AMS執(zhí)行后續(xù)流程
return finishActivityLocked(r, resultCode, resultData, reason, oomAdj, !PAUSE_IMMEDIATELY);
}
final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData,
String reason, boolean oomAdj, boolean pauseImmediately) {
if (r.finishing) {
Slog.w(TAG, "Duplicate finish request for " + r);
return false;
}
mWindowManager.deferSurfaceLayout();
try {
//設置r.finishing=true; 標記當前Activity為finishing
r.makeFinishingLocked();
final TaskRecord task = r.getTask();
EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
r.userId, System.identityHashCode(r),
task.taskId, r.shortComponentName, reason);
final ArrayList<ActivityRecord> activities = task.mActivities;
final int index = activities.indexOf(r);
if (index < (activities.size() - 1)) {
task.setFrontOfTask();
if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
// If the caller asked that this activity (and all above it)
// be cleared when the task is reset, don't lose that information,
// but propagate it up to the next activity.
ActivityRecord next = activities.get(index+1);
next.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
}
}
//停止向當前Activity分發(fā)按鍵消息事件
r.pauseKeyDispatchingLocked();
//調(diào)整當前的活動的任務棧
adjustFocusedActivityStack(r, "finishActivity");
//記錄當前Activity要發(fā)送出去的結(jié)果數(shù)據(jù)
finishActivityResultsLocked(r, resultCode, resultData);
final boolean endTask = index <= 0 && !task.isClearingToReuseTask();
final int transit = endTask ? TRANSIT_TASK_CLOSE : TRANSIT_ACTIVITY_CLOSE;
if (mResumedActivity == r) {//此處,當前Activity即為活動Activity锥惋,進入if分支
if (DEBUG_VISIBILITY || DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
"Prepare close transition: finishing " + r);
if (endTask) {
mService.mTaskChangeNotificationController.notifyTaskRemovalStarted(
task.taskId);
}
mWindowManager.prepareAppTransition(transit, false);
// Tell window manager to prepare for this one to be removed.
將當前Activity設置為不可見
r.setVisibility(false);
if (mPausingActivity == null) {//還未暫停當前Activity,因此mPausingActivity為null
//開始暫停當前Activity
startPausingLocked(false, false, null, pauseImmediately);
}
if (endTask) {
mService.getLockTaskController().clearLockedTask(task);
}
} else if (!r.isState(PAUSING)) {
...
} else {
...
}
return false;
} finally {
...
}
}
final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,
ActivityRecord resuming, boolean pauseImmediately) {
if (mPausingActivity != null) {
if (!shouldSleepActivities()) { //很明顯,正常情況這里為false
completePauseLocked(false, resuming);
}
}
ActivityRecord prev = mResumedActivity;
...
mPausingActivity = prev;
mLastPausedActivity = prev;
mLastNoHistoryActivity = (prev.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
|| (prev.info.flags & ActivityInfo.FLAG_NO_HISTORY) != 0 ? prev : null;
//設置當前Activity的狀態(tài)為pausing
prev.setState(PAUSING, "startPausingLocked");
prev.getTask().touchActiveTime();
clearLaunchTime(prev);
mStackSupervisor.getLaunchTimeTracker().stopFullyDrawnTraceIfNeeded(getWindowingMode());
mService.updateCpuStats();
if (prev.app != null && prev.app.thread != null) {
try {
EventLogTags.writeAmPauseActivity(prev.userId, System.identityHashCode(prev),
prev.shortComponentName, "userLeaving=" + userLeaving);
mService.updateUsageStats(prev, false);
//發(fā)起IPC通信劫流,通知App側(cè)暫定當前Activity大审,最終會回調(diào)Activity的onPause方法
mService.getLifecycleManager().scheduleTransaction(prev.app.thread, prev.appToken,
PauseActivityItem.obtain(prev.finishing, userLeaving,
prev.configChangeFlags, pauseImmediately));
} catch (Exception e) {
...
}
} else {
mPausingActivity = null;
mLastPausedActivity = null;
mLastNoHistoryActivity = null;
}
if (mPausingActivity != null) { //前面已經(jīng)將當前Activity賦值給mPausingActivity了徒扶,因此這里不為null
...
if (pauseImmediately) {//前面我們傳進來的參數(shù)為!PAUSE_IMMEDIATELY姜骡,因此進入else分支
// If the caller said they don't want to wait for the pause, then complete
// the pause now.
completePauseLocked(false, resuming);
return false;
} else {
//向AMS主線程發(fā)送一個延時500ms的消息,
schedulePauseTimeout(prev);
return true;
}
} else {
...
}
}
}
為了使邏輯看起來更清晰康栈,這里刪減了一部分源碼啥么,并根據(jù)調(diào)用流程悬荣,調(diào)整了方法的排列順序氯迂,同時對部分較為重要的源碼進行了注釋嚼蚀。
整個流程方法調(diào)用鏈為:在AMS中發(fā)起調(diào)用finishActivity方法,進入ActivityStack中哮独,接著依次調(diào)用了requestFinishActivityLocked -> finishActivityLocked -> finishActivityLocked -> startPausingLocked
這幾個方法舟扎。在第一個調(diào)用方法finishActivityLocked中睹限,我們詳細注釋了pauseImmediately參數(shù)的意義羡疗。最后叨恨,在startPausingLocked方法中秉颗,我們我們看到了這么一句代碼:
mService.getLifecycleManager().scheduleTransaction(prev.app.thread, prev.appToken,
PauseActivityItem.obtain(prev.finishing, userLeaving,
prev.configChangeFlags, pauseImmediately));
其實就是AMS通過Binder與App側(cè)進行IPC蚕甥,App側(cè)ActivityThread中的ApplicationThread即為本次IPC通信的對端菇怀。然后App側(cè)再通過Handler進行進程內(nèi)線程間通信敏释,通知ActivityThread中的H(繼承Handler)來處理消息义屏。H中TransactionExecutor作為真正的msg執(zhí)行者闽铐,統(tǒng)一處理消息中所攜帶的任務兄墅。處理方式即執(zhí)行PauseActivityItem中的execute和postExecute方法:
// in TransactionExecutor.java
public class TransactionExecutor {
private void executeLifecycleState(ClientTransaction transaction) {
//這里的lifecycleItem即為前面AMS傳遞過來的PauseActivityItem
final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();
...
// Execute the final transition with proper parameters.
//調(diào)用PauseActivityItem中的execute和postExecute方法
lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions);
}
}
我們進入PauseActivityItem看看AMS發(fā)起IPC究竟要干什么:
// in PauseActivityItem.java
public class PauseActivityItem extends ActivityLifecycleItem {
@Override
public void execute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
//調(diào)用ActivityThread的handlePauseActivity方法沐悦,最終會回調(diào)當前Activity的onPause
client.handlePauseActivity(token, mFinished, mUserLeaving, mConfigChanges, pendingActions,
"PAUSE_ACTIVITY_ITEM");
}
@Override
public void postExecute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
if (mDontReport) {
return;
}
try {
// TODO(lifecycler): Use interface callback instead of AMS.
// 向AMS發(fā)起IPC藏否,告知AMS當前Activity已經(jīng)暫停,可以執(zhí)行后續(xù)流程了
// IPC對端是ActivityManagerService淆储,最終AMS中的activityPaused方法會被調(diào)用
ActivityManager.getService().activityPaused(token);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}
PauseActivityItem一共干了兩件事本砰,首先是回調(diào)當前Activity的onPause青团,然后發(fā)起IPC督笆,告訴AMS可以執(zhí)行后續(xù)流程了娃肿。我們進入ActivityManagerService來看看activityPaused方法:
// in ActivityManagerService
public class ActivityManagerService extends IActivityManager.Stub
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
@Override
public final void activityPaused(IBinder token) {
final long origId = Binder.clearCallingIdentity();
synchronized(this) {
ActivityStack stack = ActivityRecord.getStackLocked(token);
if (stack != null) {
stack.activityPausedLocked(token, false);
}
}
Binder.restoreCallingIdentity(origId);
}
}
// in ActivityStack.java
class ActivityStack<T extends StackWindowController> extends ConfigurationContainer
implements StackWindowListener {
final void activityPausedLocked(IBinder token, boolean timeout) {
final ActivityRecord r = isInStackLocked(token);
if (r != null) {
//前面我們發(fā)送了一個延時500ms的消息來確保后續(xù)流程一定能夠得以執(zhí)行
//現(xiàn)在正常流程沒問題,因此移除延時消息
mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
if (mPausingActivity == r) {
mService.mWindowManager.deferSurfaceLayout();
try {
//開始執(zhí)行當前Activity暫停后的流程
//參數(shù)resumeNext為true晒杈,表示需要喚起下一個Activity
completePauseLocked(true /* resumeNext */, null /* resumingActivity */);
} finally {
mService.mWindowManager.continueSurfaceLayout();
}
return;
} else {
...
}
}
//確保全部Activity的可見性都處于正確的狀態(tài)
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
}
private void completePauseLocked(boolean resumeNext, ActivityRecord resuming) {
ActivityRecord prev = mPausingActivity;
if (prev != null) {
prev.setWillCloseOrEnterPip(false);
final boolean wasStopping = prev.isState(STOPPING);
prev.setState(PAUSED, "completePausedLocked");
if (prev.finishing) { //早在finishActivityLocked方法中就通過r.makeFinishingLocked()將finishing置為true了
//finish當前Activity
prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false,
"completedPausedLocked");
} else if (prev.app != null) {
...
} else {
...
}
if (prev != null) {
prev.stopFreezingScreenLocked(true /*force*/);
}
mPausingActivity = null;
}
if (resumeNext) { //傳遞進來的resumeNext為true
final ActivityStack topStack = mStackSupervisor.getFocusedStack();
if (!topStack.shouldSleepOrShutDownActivities()) { //看字面意思也可猜到,正常情況下進入if分支
mStackSupervisor.resumeFocusedStackTopActivityLocked(topStack, prev, null);
} else {
...
}
}
...
//再次確保全部Activity的可見性都處于正確的狀態(tài)
mStackSupervisor.ensureActivitiesVisibleLocked(resuming, 0, !PRESERVE_WINDOWS);
}
final ActivityRecord finishCurrentActivityLocked(ActivityRecord r, int mode, boolean oomAdj,
String reason) {
//獲取任務棧中棧頂Activity作為接下來即將要顯示的Activity
final ActivityRecord next = mStackSupervisor.topRunningActivityLocked(
true /* considerKeyguardState */);
...
final ActivityState prevState = r.getState();
r.setState(FINISHING, "finishCurrentActivityLocked");
//很明顯粪般,當前任務棧即為前臺活動任務棧亩歹,因此finishingActivityInNonFocusedStack為false
final boolean finishingActivityInNonFocusedStack
= r.getStack() != mStackSupervisor.getFocusedStack()
&& prevState == PAUSED && mode == FINISH_AFTER_VISIBLE;
if (mode == FINISH_IMMEDIATELY // mode為FINISH_AFTER_VISIBLE false
|| (prevState == PAUSED
&& (mode == FINISH_AFTER_PAUSE || inPinnedWindowingMode())) // inPinnedWindowingMode() 為false
|| finishingActivityInNonFocusedStack //false
|| prevState == STOPPING //prevState應該為PAUSED
|| prevState == STOPPED
|| prevState == ActivityState.INITIALIZING) {
//看起來是要銷毀當前Activity了,好開心...然而躲惰,判斷結(jié)果為false础拨,壓根不會進到if分支里面來
r.makeFinishingLocked();
boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm:" + reason);
...
}
//雖然因為條件不符合诡宗,這里還沒辦法直接銷毀當前Activity塔沃。但是蛀柴,我們先將當前Activity放到一個列表中鸽疾,該列表存儲著全部即將要銷毀的Activity
mStackSupervisor.mFinishingActivities.add(r);
r.resumeKeyDispatchingLocked();
mStackSupervisor.resumeFocusedStackTopActivityLocked();
return r;
}
}
經(jīng)過IPC冒窍,AMS側(cè)收到消息综液,調(diào)用ActivityStack的activityPausedLocked方法開始繼續(xù)后續(xù)流程谬莹。我們看到届良,這里ActivityStack中方法調(diào)用鏈為:activityPausedLocked -> completePauseLocked -> finishCurrentActivityLocked
。本來追蹤到finishCurrentActivityLocked方法送悔,看到destroyActivityLocked這句代碼時挺開心的欠啤,以為這就是終點了。然而共郭,經(jīng)過仔細分析,條件根本不符合岸蜗,整個if分支根本得不到執(zhí)行璃岳。因此铃慷,finishCurrentActivityLocked方法中僅僅是將當前Activity放到一個存儲著全部要銷毀的Activity列表里犁柜。再次回completePauseLocked方法中,由于需要喚起下一個Activity淤齐,因此稚疹,調(diào)用了
//注意參數(shù)内狗。第一個參數(shù)為當前前臺活動的任務棧柳沙,第二個參數(shù)為當前正在關閉的Activity
mStackSupervisor.resumeFocusedStackTopActivityLocked(topStack, prev, null);
這行代碼赂鲤,我們進入StackSupervisor数初,看看resumeFocusedStackTopActivityLocked方法都干了啥:
// in StackSupervisor.java
public class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener,
RecentTasks.Callbacks {
boolean resumeFocusedStackTopActivityLocked(
ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
...
if (targetStack != null && isFocusedStack(targetStack)) {
//根據(jù)傳進來的參數(shù),很容易知道進入if分支
return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
}
...
}
}
// in ActivityStack.java
class ActivityStack<T extends StackWindowController> extends ConfigurationContainer
implements StackWindowListener {
boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
...
try {
// Protect against recursion.
mStackSupervisor.inResumeTopActivity = true;
//喚起/顯示棧頂?shù)腁ctivity
result = resumeTopActivityInnerLocked(prev, options);
// When resuming the top activity, it may be necessary to pause the top activity (for
// example, returning to the lock screen. We suppress the normal pause logic in
// {@link #resumeTopActivityUncheckedLocked}, since the top activity is resumed at the
// end. We call the {@link ActivityStackSupervisor#checkReadyForSleepLocked} again here
// to ensure any necessary pause logic occurs. In the case where the Activity will be
// shown regardless of the lock screen, the call to
// {@link ActivityStackSupervisor#checkReadyForSleepLocked} is skipped.
final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
if (next == null || !next.canTurnScreenOn()) {
checkReadyForSleep();
}
} finally {
mStackSupervisor.inResumeTopActivity = false;
}
...
}
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
...
//方法很長仑鸥,內(nèi)容也很多薄料,限于篇幅摄职,這里就不仔細分析了谷市。
//主要是調(diào)用了:
if (next.app != null && next.app.thread != null) {
...
synchronized(mWindowManager.getWindowManagerLock()) {
try {
...
//發(fā)起IPC迫悠,通知App側(cè)要打開的Activity创泄,最終會回調(diào)下一個要打開的Activity的onResume
transaction.setLifecycleStateRequest(
ResumeActivityItem.obtain(next.app.repProcState,
mService.isNextTransitionForward()));
mService.getLifecycleManager().scheduleTransaction(transaction);
} catch (Exception e) {
...
}
}
try {
// 除了通知App側(cè)下一個要打開的Activity,AMS側(cè)也要完成其他狀態(tài)設置相關的流程
next.completeResumeLocked();
} catch (Exception e) {
// If any exception gets thrown, toss away this
// activity and try the next one.
Slog.w(TAG, "Exception thrown during resume of " + next, e);
requestFinishActivityLocked(next.appToken, Activity.RESULT_CANCELED, null,
"resume-exception", true);
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
...
}
...
}
}
至此搁拙,我們需要兵分兩路箕速。一路是追蹤AMS向App發(fā)起的IPC盐茎,喚起下一個要顯示的Activity庭呜;一路是追蹤喚起下一個Activity后AMS本身要進行的狀態(tài)設置相關工作。我們先跟蹤IPC流程阴汇,ResumeActivityItem與前面我們分析的PauseActivityItem一樣搀庶,都是先執(zhí)行execute方法哥倔,再執(zhí)行postExecute方法:
// in ResumeActivityItem.java
public class ResumeActivityItem extends ActivityLifecycleItem {
@Override
public void execute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
//調(diào)用ActivityThread的handleResumeActivity方法东抹,最終會回調(diào)下一個要顯示的Activity的onResume
client.handleResumeActivity(token, true /* finalStateRequest */, mIsForward,
"RESUME_ACTIVITY");
}
@Override
public void postExecute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
try {
// TODO(lifecycler): Use interface callback instead of AMS.
// 向AMS發(fā)起IPC,告知AMS下一個要顯示的Activity已經(jīng)resume蒂破,可以執(zhí)行后續(xù)流程了
// IPC對端是ActivityManagerService惧互,最終AMS的activityResumed方法會被調(diào)用
ActivityManager.getService().activityResumed(token);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}
ResumeActivityItem也就干了兩件事喊儡,首先是回調(diào)下一個要顯示的Activity的onResume管宵,然后發(fā)起IPC箩朴,告訴AMS可以執(zhí)行后續(xù)流程了炸庞。我們再次進入ActivityManagerService來看看activityResumed方法:
// in ActivityManagerService
public class ActivityManagerService extends IActivityManager.Stub
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
@Override
public final void activityResumed(IBinder token) {
final long origId = Binder.clearCallingIdentity();
synchronized(this) {
ActivityRecord.activityResumedLocked(token);
mWindowManager.notifyAppResumedFinished(token);
}
Binder.restoreCallingIdentity(origId);
}
}
如果從AMS再繼續(xù)跟蹤下去,線索就斷了滥壕。千辛萬苦分析到此處绎橘,結(jié)果還是沒能挖出真相称鳞?心里一萬頭草泥馬呼嘯而過。沒辦法狂票,生活還是要繼續(xù)闺属,讓我們收拾好心情屋剑,回溯到上一個方法唉匾,看看ActivityThread的handleResumeActivity方法都干了些啥:
// in ActivityThread.java
public final class ActivityThread extends ClientTransactionHandler {
@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
...
往主線程的消息隊列添加了一個空閑處理器巍膘,當主線程空閑時就會回調(diào)這個處理器來執(zhí)行一些優(yōu)先級較低的任務
Looper.myQueue().addIdleHandler(new Idler());
}
private class Idler implements MessageQueue.IdleHandler {
@Override
public final boolean queueIdle() {
ActivityClientRecord a = mNewActivities;
boolean stopProfiling = false;
if (mBoundApplication != null && mProfiler.profileFd != null
&& mProfiler.autoStopProfiler) {
stopProfiling = true;
}
if (a != null) {
mNewActivities = null;
IActivityManager am = ActivityManager.getService();
ActivityClientRecord prev;
do {
if (localLOGV) Slog.v(
TAG, "Reporting idle of " + a +
" finished=" +
(a.activity != null && a.activity.mFinished));
if (a.activity != null && !a.activity.mFinished) {
try {
//發(fā)起IPC肪康,告訴AMS側(cè)磷支,我(App側(cè))空閑了,你有什么事情需要我做的抵皱,趕緊扔過來吧
//最終會調(diào)用ActivityManagerService的activityIdle方法
am.activityIdle(a.token, a.createdConfig, stopProfiling);
a.createdConfig = null;
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
prev = a;
a = a.nextIdle;
prev.nextIdle = null;
} while (a != null);
}
...
}
}
}
關鍵的代碼我已經(jīng)注釋起來了移盆,總而言之就是App在下一個要顯示的Activity調(diào)用完onResume后往自己的主線程消息隊列注冊一個空閑處理器味滞,以便App主線程的消息隊列中全部msg處理完了以后能去處理一些低優(yōu)先級的任務爽醋。這個空閑處理器的調(diào)用是一次性的蚂四,也就是說調(diào)用一次之后就不會再被調(diào)用了遂赠。而這個所謂的低優(yōu)先級任務筷弦,其實就是App側(cè)向AMS側(cè)發(fā)起IPC烂琴,告訴AMS,我(App)閑下來了号醉,你有什么需要我干的任務沒有畔派。進入ActivityManagerService父虑,看看activityIdle方法:
//in ActivityManagerService
public class ActivityManagerService extends IActivityManager.Stub
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
@Override
public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
final long origId = Binder.clearCallingIdentity();
synchronized (this) {
ActivityStack stack = ActivityRecord.getStackLocked(token);
if (stack != null) {
//App側(cè)空閑了士嚎,要干點什么好呢莱衩?
ActivityRecord r =
mStackSupervisor.activityIdleInternalLocked(token, false /* fromTimeout */,
false /* processPausingActivities */, config);
if (stopProfiling) {
if ((mProfileProc == r.app) && mProfilerInfo != null) {
clearProfilerLocked();
}
}
}
}
Binder.restoreCallingIdentity(origId);
}
}
//in StackSupervisor
public class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener,
RecentTasks.Callbacks {
final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
boolean processPausingActivities, Configuration config) {
...
// Finish any activities that are scheduled to do so but have been
// waiting for the next one to start.
//還記得前面我們分析的Activity銷毀流程吧,
//在ActivityStack的finishCurrentActivityLocked方法中括细,眼看就要成功,卻因條件不符功虧一簣的地方嗎呆盖?
//雖然條件不符应又,未能調(diào)用destroyActivityLocked去銷毀Activity株扛,但當時卻將需要待銷毀的Activity加入到一個列表中了。
//此處的finishes就是那個待銷毀的Activity列表
for (int i = 0; i < NF; i++) {
r = finishes.get(i);
final ActivityStack stack = r.getStack();
if (stack != null) {
activityRemoved |= stack.destroyActivityLocked(r, true, "finish-idle");
}
}
...
}
}
分析這么久奖磁,終于看到希望的曙光了咖为。再接再厲,進入ActivityStack吞彤,速度瞟一眼destroyActivityLocked方法吧:
// in ActivityStack.java
class ActivityStack<T extends StackWindowController> extends ConfigurationContainer
implements StackWindowListener {
final boolean destroyActivityLocked(ActivityRecord r, boolean removeFromApp, String reason) {
...
final boolean hadApp = r.app != null;
if (hadApp) {
...
boolean skipDestroy = false;
try {
//發(fā)起IPC,通知App側(cè)銷毀ActivityB袂丁1⑧隆了罪!
mService.getLifecycleManager().scheduleTransaction(r.app.thread, r.appToken,
DestroyActivityItem.obtain(r.finishing, r.configChangeFlags));
} catch (Exception e) {
...
}
r.nowVisible = false;
// If the activity is finishing, we need to wait on removing it
// from the list to give it a chance to do its cleanup. During
// that time it may make calls back with its token so we need to
// be able to find it on the list and so we don't want to remove
// it from the list yet. Otherwise, we can just immediately put
// it in the destroyed state since we are not removing it from the
// list.
if (r.finishing && !skipDestroy) {
r.setState(DESTROYING,
"destroyActivityLocked. finishing and not skipping destroy");
//向AMS主線程發(fā)起一個延時10s的消息田藐,保證即使正常流程失敗踊餐,也能進行Activity銷毀的后續(xù)流程
//這里也是10s,頗具誤導性窜管。
//在這里繞了好久時間幕帆,最終發(fā)現(xiàn)失乾,這個分支流程與我們要分析的問題關系不大
Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG, r);
mHandler.sendMessageDelayed(msg, DESTROY_TIMEOUT);
} else {
r.setState(DESTROYED,
"destroyActivityLocked. not finishing or skipping destroy");
r.app = null;
}
} else {
...
}
}
}
老規(guī)矩,讓我們看看AMS發(fā)起IPC纽竣,通知App側(cè)都干了啥。進入DestroyActivityItem茧泪,看看execute方法和postExecute方法:
public class DestroyActivityItem extends ActivityLifecycleItem {
@Override
public void execute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
//調(diào)用ActivityThread的handleDestroyActivity蜓氨,最終會調(diào)用Activity的onDestroy方法
client.handleDestroyActivity(token, mFinished, mConfigChanges,
false /* getNonConfigInstance */, "DestroyActivityItem");
}
}
So easy~。呃调炬,postExecute方法哪去了语盈?簡單,我們一路跟蹤DestroyActivityItem的父類刀荒,會發(fā)現(xiàn)postExecute默認實現(xiàn)為一個空方法。
那么,整個分析算是完成了吧。
終于完了啊徽缚,讓我放松下喘口氣先~~
等等D峭瘛:墼ⅰ!onStop方法怎么沒被調(diào)用童谒??為什么回調(diào)會延時??盛嘿?為什么會延時10s??糕伐?
蒼天啊赞庶,搞了這么久,原來問題一個都還沒解決呢??哈哈,其實不是,現(xiàn)在我們離真相只有一步之遙了,一個一個來。
- 首先說下onStop方法什么時候被調(diào)用的。
DestroyActivityItem的execute和postExecute方法誰調(diào)用的啊欠动?前面的分析還沒忘吧,是在TransactionExecutor的executeLifecycleState方法中調(diào)用的:
看到?jīng)],關鍵代碼是// in TransactionExecutor.java private void executeLifecycleState(ClientTransaction transaction) { // Cycle to the state right before the final requested state. //根據(jù)Activity當前的狀態(tài)和最終要到達的狀態(tài)靴迫,計算中間需要經(jīng)過哪些步驟 cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */); // Execute the final transition with proper parameters. lifecycleItem.execute(mTransactionHandler, token, mPendingActions); lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions); }
cycleToPath()
参淫,只要稍微一跟蹤就可以知道在Pause和Destroy之間會執(zhí)行Stop了跌前,這個流程簡單沒有其他分支掏缎,因此就不詳寫了忌怎。 - onStop和onDestroy回調(diào)為什么會延時
前面我們已經(jīng)分析完了調(diào)用finish后,到回調(diào)onStop和onDestroy之間的整個正常流程璃弄。這中間經(jīng)歷了多次App側(cè)和AMS側(cè)的進程間通信酌壕,以及兩端各自進程內(nèi)通信。正常流程中較為關鍵的一點是,新的要顯示的Activity在resume后遮怜,App側(cè)的主線程空閑下來才會通知AMS執(zhí)行后續(xù)流程初橘,將關閉的Activity銷毀扔亥。那要是新的Activity在resume后,主線程一直在循環(huán)處理MessageQueue中堆積的msg呢薛训?很顯然辙喂,要是這樣的話,通知AMS執(zhí)行后續(xù)流程自然被延遲了女气,因此,Activity的onStop和onDestroy生命周期回調(diào)自然也就被延遲了。 - 延時10s是為什么
作為一個健壯的操作系統(tǒng)焦读,當然要有一定的容錯機制兆衅,不能說因為App側(cè)主線程一直忙专挪,AMS側(cè)就不去銷毀/回收已經(jīng)死亡的Activity家卖。要不然新手開發(fā)者開發(fā)的App會因為內(nèi)存泄漏等問題分分鐘玩死系統(tǒng)金刁。那么這個容錯機制是怎么設計的呢醇锚?
前面我們說兵分兩路,一路是追蹤AMS向App發(fā)起的IPC但两,喚起下一個要顯示的Activity训裆,一路是追蹤喚起下一個Activity后AMS本身要進行的狀態(tài)設置相關工作。分析完了AMS側(cè)向App側(cè)發(fā)起IPC后的正常流程后蜀铲,我們繼續(xù)分析AMS本身還做了哪些工作://in ActivityStack.java private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) { next.completeResumeLocked(); }
// in ActivityRecord.java final class ActivityRecord extends ConfigurationContainer implements AppWindowContainerListener { void completeResumeLocked() { ... // Schedule an idle timeout in case the app doesn't do it for us. // 設置一個超時的空閑時間边琉,以便在App沒有通知我們其有空的情形下也能執(zhí)行相關流程 mStackSupervisor.scheduleIdleTimeoutLocked(this); ... } }
// in StackSupervisor.java public class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener, RecentTasks.Callbacks { void scheduleIdleTimeoutLocked(ActivityRecord next) { //真相就在這里,發(fā)送一個延時10s的消息记劝,確保正常流程行不通的情況下也能銷毀Activity Message msg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG, next); mHandler.sendMessageDelayed(msg, IDLE_TIMEOUT); } private final class ActivityStackSupervisorHandler extends Handler { void activityIdleInternal(ActivityRecord r, boolean processPausingActivities) { synchronized (mService) { //在前面我們分析正常流程的時候变姨,已經(jīng)將該方法的執(zhí)行流程分析完了,不再贅述 activityIdleInternalLocked(r != null ? r.appToken : null, true /* fromTimeout */, processPausingActivities, null); } } @Override public void handleMessage(Message msg) { switch (msg.what) { case IDLE_TIMEOUT_MSG: { //延時10s后厌丑,就當Activity側(cè)已經(jīng)空閑下來了定欧,執(zhí)行后續(xù)流程 activityIdleInternal((ActivityRecord) msg.obj, true /* processPausingActivities */); } break; ... } } }
好了别伏。這下是真的分析完了。
因為源碼是在太長了忧额,因此省略了很多分支流程和與主題無關的代碼。
說是分析回調(diào)延時的問題愧口,其實也即是分析finish的源碼調(diào)用流程睦番。通過分析,我們知道耍属,要想使得Activity的onStop和onDestroy盡快得到回調(diào)托嚣,我們就該在寫代碼的時候,及時關閉厚骗、清理示启、移除不必要的主線程消息,并且盡可能的保證每個消息處理時間不要太長领舰。
經(jīng)過這一波分析夫嗓,該對Framework層App和AMS的交互有了更清晰的理解了吧。
也算是給自己一個交代冲秽,完美舍咖!下班~