Android 面試進階指南目錄java
計算機網絡android
Android程序員
交流羣裏碰到一個頗有意思的問題,調用 Activity.finish() 以後 10s 纔回調 onDestroy() 。 由此產生了一些不可控問題,例如在 onDestroy()
中釋放資源不及時,賦值狀態異常等等。我以前倒沒有遇到過相似的問題,可是 AOSP 老是咱們最好的老師。從 Activity.finish()
開始擼了一遍流程,找到了問題的答案。面試
在讀源碼以前,咱們先來複現一下 10s onDestroy 的場景。寫一個最簡單的 FirstActivity
跳轉到 SecondActivity
的場景,並記錄下各個生命週期和調用 finish()
的時間間隔。數組
class FirstActivity : BaseLifecycleActivity() {
private val binding by lazy { ActivityFirstBinding.inflate(layoutInflater) }
var startTime = 0L
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
binding.goToSecond.setOnClickListener {
start<SecondActivity>()
finish()
startTime = System.currentTimeMillis()
}
}
override fun onPause() {
super.onPause()
Log.e("finish","onPause() 距離 finish() :${System.currentTimeMillis() - startTime} ms")
}
override fun onStop() {
super.onStop()
Log.e("finish","onStop() 距離 finish() :${System.currentTimeMillis() - startTime} ms")
}
override fun onDestroy() {
super.onDestroy()
Log.e("finish","onDestroy() 距離 finish() :${System.currentTimeMillis() - startTime} ms")
}
}
複製代碼
SecondActivity
是一個普通的沒有進行任何操做的空白 Activity 。點擊按鈕跳轉到 SecondActivity,打印日誌以下:markdown
FirstActivity: onPause,onPause() 距離 finish() :5 ms
SecondActivity: onCreate
SecondActivity: onStart
SecondActivity: onResume
FirstActivity: onStop,onStop() 距離 finish() :660 ms
FirstActivity: onDestroy,onDestroy() 距離 finish() :663 ms
複製代碼
能夠看到正常狀況下,FirstActivity 回調 onPause 以後,SecondActivity 開始正常的生命週期流程,直到 onResume 被回調,對用戶可見時,FirstActivity 纔會回調 onPause 和 onDestroy 。時間間隔也都在正常範圍之內。網絡
咱們再模擬一個在 SecondActivity 啓動時進行大量動畫的場景,源源不斷的向主線程消息隊列塞消息。修改一下 SecondActivity 的代碼。app
class SecondActivity : BaseLifecycleActivity() {
private val binding by lazy { ActivitySecondBinding.inflate(layoutInflater) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
postMessage()
}
private fun postMessage() {
binding.secondBt.post {
Thread.sleep(10)
postMessage()
}
}
}
複製代碼
再來看一下日誌:異步
FirstActivity: onPause, onPause() 距離 finish() :6 ms
SecondActivity: onCreate
SecondActivity: onStart
SecondActivity: onResume
FirstActivity: onStop, onStop() 距離 finish() :10033 ms
FirstActivity: onDestroy, onDestroy() 距離 finish() :10037 ms
複製代碼
FirstActivity 的 onPause() 沒有受到影響。由於在 Activity 跳轉過程當中,目標 Activity 只有在前一個 Activity onPause()
以後纔會開始正常的生命週期。而 onStop
和 onDestroy()
整整過了 10s 纔回調。ide
對比以上兩個場景,咱們能夠猜想,當 SecondActivity 的主線程過於繁忙,沒有機會停下來喘口氣的時候,會形成 FirstActivity 沒法及時回調 onStop
和 onDestroy
。基於以上猜想,咱們就能夠從 AOSP 中來尋找答案了。
接下來就是大段的枯燥的源碼分析了。帶着問題去讀 AOSP,可讓這個過程不是那麼 「枯燥」,並且必定會有不少不同的收穫。
如下源代碼基於 Android 9.0 版本。
> Activity.java public void finish() {
finish(DONT_FINISH_TASK_WITH_ACTIVITY);
}
複製代碼
重載了帶參數的 finish() 方法。參數是 DONT_FINISH_TASK_WITH_ACTIVITY
,含義也很直白,不會銷燬 Activity 所在的任務棧。
> Activity.java private void finish(int finishTask) {
// mParent 通常爲 null,在 ActivityGroup 中會使用到
if (mParent == null) {
......
try {
// Binder 調用 AMS.finishActivity()
if (ActivityManager.getService()
.finishActivity(mToken, resultCode, resultData, finishTask)) {
mFinished = true;
}
} catch (RemoteException e) {
}
} else {
mParent.finishFromChild(this);
}
......
}
複製代碼
這裏的 mParent
大多數狀況下都是 null ,不須要考慮 else 分支的狀況。一些大齡 Android 程序員可能會了解 ActivityGroup,在此種狀況下 mParent 可能會不爲 null。(由於我還年輕,因此沒有使用過 ActivityGroup,就不過多解釋了。)其中 Binder 調用了 AMS.finishActivity()
方法。
> ActivityManagerService.java public final boolean finishActivity(IBinder token, int resultCode, Intent resultData, int finishTask) {
......
synchronized(this) {
// token 持有 ActivityRecord 的弱引用
ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r == null) {
return true;
}
......
try {
boolean res;
final boolean finishWithRootActivity =
finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY;
// finishTask 參數是 DONT_FINISH_TASK_WITH_ACTIVITY,進入 else 分支
if (finishTask == Activity.FINISH_TASK_WITH_ACTIVITY
|| (finishWithRootActivity && r == rootR)) {
res = mStackSupervisor.removeTaskByIdLocked(tr.taskId, false,
finishWithRootActivity, "finish-activity");
} else {
// 調用 ActivityStack.requestFinishActivityLocked()
res = tr.getStack().requestFinishActivityLocked(token, resultCode,
resultData, "app-request", true);
}
return res;
} finally {
Binder.restoreCallingIdentity(origId);
}
}
}
複製代碼
注意方法參數中的 token
對象,在上一篇文章 爲何不能使用 Application Context 顯示 Dialog? 中詳細介紹過,Token 是 ActivityRecord 的靜態內部類,它持有外部 ActivityRecord 的弱引用。繼承自 IApplicationToken.Stub ,是一個 Binder 對象。ActivityRecord 就是對當前 Activity 的具體描述,包含了 Activity 的全部信息。
傳入的 finishTask() 方法的參數是 DONT_FINISH_TASK_WITH_ACTIVITY
,因此接着會調用 ActivityStack.requestFinishActivityLocked()
方法。
> ActivityStack.java final boolean requestFinishActivityLocked(IBinder token, int resultCode, Intent resultData, String reason, boolean oomAdj) {
ActivityRecord r = isInStackLocked(token);
if (r == null) {
return false;
}
finishActivityLocked(r, resultCode, resultData, reason, oomAdj);
return true;
}
final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData, String reason, boolean oomAdj) {
// PAUSE_IMMEDIATELY 爲 true,在 ActivityStackSupervisor 中定義
return finishActivityLocked(r, resultCode, resultData, reason, oomAdj, !PAUSE_IMMEDIATELY);
}
複製代碼
最後調用的是一個重載的 finishActivityLocked()
方法。
> ActivityStack.java
// 參數 pauseImmediately 是 false
final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData, String reason, boolean oomAdj, boolean pauseImmediately) {
if (r.finishing) { // 重複 finish 的狀況
return false;
}
mWindowManager.deferSurfaceLayout();
try {
// 標記 r.finishing = true,
// 前面會作重複 finish 的檢測就是依賴這個值
r.makeFinishingLocked();
final TaskRecord task = r.getTask();
......
// 暫停事件分發
r.pauseKeyDispatchingLocked();
adjustFocusedActivityStack(r, "finishActivity");
// 處理 activity result
finishActivityResultsLocked(r, resultCode, resultData);
// mResumedActivity 就是當前 Activity,會進入此分支
if (mResumedActivity == r) {
......
// Tell window manager to prepare for this one to be removed.
r.setVisibility(false);
if (mPausingActivity == null) {
// 開始 pause mResumedActivity
startPausingLocked(false, false, null, pauseImmediately);
}
......
} else if (!r.isState(PAUSING)) {
// 不會進入此分支
......
}
return false;
} finally {
mWindowManager.continueSurfaceLayout();
}
}
複製代碼
調用 finish 以後確定是要先 pause 當前 Activity,沒毛病。接着看 startPausingLocked()
方法。
> ActivityStack.java final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, ActivityRecord resuming, boolean pauseImmediately) {
......
ActivityRecord prev = mResumedActivity;
if (prev == null) {
// 沒有 onResume 的 Activity,不能執行 pause
if (resuming == null) {
mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
return false;
}
......
mPausingActivity = prev;
// 設置當前 Activity 狀態爲 PAUSING
prev.setState(PAUSING, "startPausingLocked");
......
if (prev.app != null && prev.app.thread != null) {
try {
......
// 1. 經過 ClientLifecycleManager 分發生命週期事件
// 最終會向 H 發送 EXECUTE_TRANSACTION 事件
mService.getLifecycleManager().scheduleTransaction(prev.app.thread, prev.appToken,
PauseActivityItem.obtain(prev.finishing, userLeaving,
prev.configChangeFlags, pauseImmediately));
} catch (Exception e) {
mPausingActivity = null;
}
} else {
mPausingActivity = null;
}
......
// mPausingActivity 在前面已經賦值,就是當前 Activity
if (mPausingActivity != null) {
......
if (pauseImmediately) { // 這裏是 false,進入 else 分支
completePauseLocked(false, resuming);
return false;
} else {
// 2. 發送一個延時 500ms 的消息,等待 pause 流程一點時間
// 最終會回調 activityPausedLocked() 方法
schedulePauseTimeout(prev);
return true;
}
} else {
// 不會進入此分支
}
}
複製代碼
這裏面有兩步重點操做。第一步是註釋 1 處經過 ClientLifecycleManager 分發生命週期流程。第二步是發送一個延時 500ms 的消息,等待一下 onPause 流程。可是若是第一步中在 500ms 內已經完成了流程,則會取消這個消息。因此這兩步的最終邏輯實際上是一致的。這裏就直接看第一步。
mService.getLifecycleManager().scheduleTransaction(prev.app.thread, prev.appToken,
PauseActivityItem.obtain(prev.finishing, userLeaving,
prev.configChangeFlags, pauseImmediately));
複製代碼
ClientLifecycleManager
我在以前的一篇文章 從源碼看 Activity 生命週期(上篇) 作過詳細介紹。它會向主線程的 Handler H 發送 EXECUTE_TRANSACTION 事件,調用 XXXActivityItem
的 execute()
和 postExecute()
方法。execute()
方法中會 Binder 調用 ActivityThread
中對應的 handleXXXActivity()
方法。在這裏就是 handlePauseActivity()
方法,其中會經過 Instrumentation.callActivityOnPause(r.activity)
方法回調 Activity.onPause()
。
> Instrumentation.java public void callActivityOnPause(Activity activity) {
activity.performPause();
}
複製代碼
到這裏,onPause()
方法就被執行了。可是流程沒有結束,接着就該顯示下一個 Activity 了。前面剛剛說過會調用 PauseActivityItem
的 execute()
和 postExecute()
方法。execute()
方法回調了當前 Activity.onPause()
,而 postExecute()
方法就是去尋找要顯示的 Activity 。
> PauseActivityItem.java public void postExecute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) {
try {
ActivityManager.getService().activityPaused(token);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
複製代碼
Binder 調用了 AMS.activityPaused()
方法。
> ActivityManagerService.java public final void activityPaused(IBinder token) {
synchronized(this) {
ActivityStack stack = ActivityRecord.getStackLocked(token);
if (stack != null) {
stack.activityPausedLocked(token, false);
}
}
}
複製代碼
調用了 ActivityStack.activityPausedLocked()
方法。
> ActivityStack.java final void activityPausedLocked(IBinder token, boolean timeout) {
final ActivityRecord r = isInStackLocked(token);
if (r != null) {
// 看這裏
mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
if (mPausingActivity == r) {
mService.mWindowManager.deferSurfaceLayout();
try {
// 看這裏
completePauseLocked(true /* resumeNext */, null /* resumingActivity */);
} finally {
mService.mWindowManager.continueSurfaceLayout();
}
return;
} else {
// 不會進入 else 分支
}
}
}
複製代碼
上面有這麼一行代碼 mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r)
,移除的就是以前延遲 500ms 的消息。接着看 completePauseLocked()
方法。
> ActivityStack.java private void completePauseLocked(boolean resumeNext, ActivityRecord resuming) {
ActivityRecord prev = mPausingActivity;
if (prev != null) {
// 設置狀態爲 PAUSED
prev.setState(PAUSED, "completePausedLocked");
if (prev.finishing) { // 1. finishing 爲 true,進入此分支
prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false,
"completedPausedLocked");
} else if (prev.app != null) {
// 不會進入此分支
} else {
prev = null;
}
......
}
if (resumeNext) {
// 當前獲取焦點的 ActivityStack
final ActivityStack topStack = mStackSupervisor.getFocusedStack();
if (!topStack.shouldSleepOrShutDownActivities()) {
// 2. 恢復要顯示的 activity
mStackSupervisor.resumeFocusedStackTopActivityLocked(topStack, prev, null);
} else {
checkReadyForSleep();
ActivityRecord top = topStack.topRunningActivityLocked();
if (top == null || (prev != null && top != prev)) {
mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
}
}
......
}
複製代碼
這裏分了兩步走。註釋1 處判斷了 finishing
狀態,還記得 finishing 在何處被賦值爲 true
的嗎?在 Activity.finish() -> AMS.finishActivity() -> ActivityStack.requestFinishActivityLocked() -> ActivityStack.finishActivityLocked()
方法中。因此接着調用的是 finishCurrentActivityLocked()
方法。註釋2 處就是來顯示應該顯示的 Activity ,就再也不追進去細看了。
再跟到 finishCurrentActivityLocked()
方法中,看這名字,確定是要 stop/destroy 沒跑了。
> ActivityStack.java
/* * 把前面帶過來的參數標出來 * prev, FINISH_AFTER_VISIBLE, false,"completedPausedLocked" */
final ActivityRecord finishCurrentActivityLocked(ActivityRecord r, int mode, boolean oomAdj, String reason) {
// 獲取將要顯示的棧頂 Activity
final ActivityRecord next = mStackSupervisor.topRunningActivityLocked(
true /* considerKeyguardState */);
// 1. mode 是 FINISH_AFTER_VISIBLE,進入此分支
if (mode == FINISH_AFTER_VISIBLE && (r.visible || r.nowVisible)
&& next != null && !next.nowVisible) {
if (!mStackSupervisor.mStoppingActivities.contains(r)) {
// 加入到 mStackSupervisor.mStoppingActivities
addToStopping(r, false /* scheduleIdle */, false /* idleDelayed */);
}
// 設置狀態爲 STOPPING
r.setState(STOPPING, "finishCurrentActivityLocked");
return r;
}
......
// 下面會執行 destroy,可是代碼並不能執行到這裏
if (mode == FINISH_IMMEDIATELY
|| (prevState == PAUSED
&& (mode == FINISH_AFTER_PAUSE || inPinnedWindowingMode()))
|| finishingActivityInNonFocusedStack
|| prevState == STOPPING
|| prevState == STOPPED
|| prevState == ActivityState.INITIALIZING) {
boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm:" + reason);
......
return activityRemoved ? null : r;
}
......
}
複製代碼
註釋 1 處 mode
的值是 FINISH_AFTER_VISIBLE
,而且如今新的 Activity 尚未 onResume
,因此 r.visible || r.nowVisible
和 next != null && !next.nowVisible
都是成立的,並不會進入後面的 destroy 流程。雖然看到這還沒獲得想要的答案,可是起碼是符合預期的。若是在這就直接 destroy 了,延遲 10s 才 onDestroy 的問題就無疾而終了。
對於這些暫時還不銷燬的 Activity 都執行了 addToStopping(r, false, false)
方法。咱們繼續追進去。
> ActivityStack.java void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed) {
if (!mStackSupervisor.mStoppingActivities.contains(r)) {
mStackSupervisor.mStoppingActivities.add(r);
......
}
......
// 省略的代碼中,對 mStoppingActivities 的存儲容量作了限制。超出限制可能會提早出發銷燬流程
}
複製代碼
這些在等待銷燬的 Activity 被保存在了 ActivityStackSupervisor
的 mStoppingActivities
集合中,它是一個 ArrayList<ActivityRecord>
。
整個 finish 流程就到此爲止了。前一個 Activity 被保存在了 ActivityStackSupervisor.mStoppingActivities
集合中,新的 Activity 被顯示出來了。
問題彷佛進入了困境,何時回調 onStop/onDestroy
呢?其實這個纔是根本問題。上面擼了一遍 finish() 並看不到本質,可是能夠幫助咱們造成一個完整的流程,這個一直是看 AOSP 最大的意義,幫助咱們把零碎的上層知識造成一個完整的閉環。
回到正題來,在 Activity 跳轉過程當中,爲了保證流暢的用戶體驗,只要前一個 Activity 與用戶不可交互,即 onPause() 被回調以後,下一個 Activity 就要開始本身的生命週期流程了。因此 onStop/onDestroy
的調用時間是不肯定的,甚至像文章開頭的例子中,整整過了 10s 纔回調。那麼,究竟是由誰來驅動 onStop/onDestroy
的執行呢?咱們來看看下一個 Activity 的 onResume 過程。
直接看 ActivityThread.handleResumeActivity()
方法,相信你們對生命週期的調用流程也很熟悉了。
> ActivityThread.java public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String reason) {
......
// 回調 onResume
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
......
final Activity a = r.activity;
......
if (r.window == null && !a.mFinished && willBeVisible) {
......
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
// 添加 decorView 到 WindowManager
wm.addView(decor, l);
} else {
a.onWindowAttributesChanged(l);
}
}
} else if (!willBeVisible) {
......
}
......
// 主線程空閒時會執行 Idler
Looper.myQueue().addIdleHandler(new Idler());
}
複製代碼
handleResumeActivity()
方法是整個 UI 顯示流程的重中之重,它首先會回調 Activity.onResume()
, 而後將 DecorView 添加到 Window 上,其中又包括了建立 ViewRootImpl,建立 Choreographer,與 WMS 進行 Binder 通訊,註冊 vsync 信號,著名的 measure/draw/layout。這一塊的源碼真的很值得一讀,不過不是這篇文章的重點,後面會單獨來捋一捋。
在完成最終的界面繪製和顯示以後,有這麼一句代碼 Looper.myQueue().addIdleHandler(new Idler())
。IdleHandler
不知道你們是否熟悉,它提供了一種機制,當主線程消息隊列空閒時,會執行 IdleHandler 的回調方法。至於怎麼算 「空閒」,咱們能夠看一下 MessageQueue.next()
方法。
> MessageQueue.java Message next() {
......
int pendingIdleHandlerCount = -1;
int nextPollTimeoutMillis = 0;
for (;;) {
// 阻塞方法,主要是經過 native 層的 epoll 監聽文件描述符的寫入事件來實現的。
// 若是 nextPollTimeoutMillis = -1,一直阻塞不會超時。
// 若是 nextPollTimeoutMillis = 0,不會阻塞,當即返回。
// 若是 nextPollTimeoutMillis > 0,最長阻塞nextPollTimeoutMillis毫秒(超時),若是期間有程序喚醒會當即返回。
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// msg.target == null表示此消息爲消息屏障(經過postSyncBarrier方法發送來的)
// 若是發現了一個消息屏障,會循環找出第一個異步消息(若是有異步消息的話),全部同步消息都將忽略(日常發送的通常都是同步消息)
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// 消息觸發時間未到,設置下一次輪詢的超時時間
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 獲得 Message
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
msg.markInUse(); // 標記 FLAG_IN_USE
return msg;
}
} else {
nextPollTimeoutMillis = -1;
}
......
/* * 兩個條件: * 1. pendingIdleHandlerCount = -1 * 2. 這次取到的 mMessage 爲空或者須要延遲處理 */
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// 沒有 idle handler 須要運行,繼續循環
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 下一次 next 時,pendingIdleHandlerCount 又會被置爲 -1,不會致使死循環
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
// 執行 Idler
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// 將 pendingIdleHandlerCount 置零
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;
}
}
複製代碼
在正常的消息處理機制以後,額外對 IdleHandler 進行了處理。當本次取到的 Message 爲空或者須要延時處理的時候,就會去執行 mIdleHandlers
數組中的 IdleHandler 對象。其中還有一些關於 pendingIdleHandlerCount 的額外邏輯來防止循環處理。
因此,不出意外的話,當新的 Activity 完成頁面繪製並顯示以後,主線程就能夠停下歇一歇,來執行 IdleHandler
了。再回來 handleResumeActivity()
中來,Looper.myQueue().addIdleHandler(new Idler())
,這裏的 Idler
是 IdleHandler
的一個具體實現類。
> ActivityThread.java
private class Idler implements MessageQueue.IdleHandler {
@Override
public final boolean queueIdle() {
ActivityClientRecord a = mNewActivities;
......
}
if (a != null) {
mNewActivities = null;
IActivityManager am = ActivityManager.getService();
ActivityClientRecord prev;
do {
if (a.activity != null && !a.activity.mFinished) {
try {
// 調用 AMS.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);
}
......
return false;
}
}
複製代碼
Binder 調用了 AMS.activityIdle()
。
> ActivityManagerService.java 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) {
ActivityRecord r =
mStackSupervisor.activityIdleInternalLocked(token, false /* fromTimeout */,
false /* processPausingActivities */, config);
......
}
}
}
複製代碼
調用了 ActivityStackSupervisor.activityIdleInternalLocked()
方法。
> ActivityStackSupervisor.java final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout, boolean processPausingActivities, Configuration config) {
ArrayList<ActivityRecord> finishes = null;
ArrayList<UserState> startingUsers = null;
int NS = 0;
int NF = 0;
boolean booting = false;
boolean activityRemoved = false;
ActivityRecord r = ActivityRecord.forTokenLocked(token);
......
// 獲取要 stop 的 Activity
final ArrayList<ActivityRecord> stops = processStoppingActivitiesLocked(r,
true /* remove */, processPausingActivities);
NS = stops != null ? stops.size() : 0;
if ((NF = mFinishingActivities.size()) > 0) {
finishes = new ArrayList<>(mFinishingActivities);
mFinishingActivities.clear();
}
// 該 stop 的 stop
for (int i = 0; i < NS; i++) {
r = stops.get(i);
final ActivityStack stack = r.getStack();
if (stack != null) {
if (r.finishing) {
stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false,
"activityIdleInternalLocked");
} else {
stack.stopActivityLocked(r);
}
}
}
// 該 destroy 的 destroy
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");
}
}
......
return r;
}
複製代碼
stops
和 finishes
分別是要 stop 和 destroy 的兩個 ActivityRecord 數組。stops
數組是經過 ActivityStackSuperVisor.processStoppingActivitiesLocked()
方法獲取的,追進去看一下。
> ActivityStackSuperVisor.java final ArrayList<ActivityRecord> processStoppingActivitiesLocked(ActivityRecord idleActivity, boolean remove, boolean processPausingActivities) {
ArrayList<ActivityRecord> stops = null;
final boolean nowVisible = allResumedActivitiesVisible();
// 遍歷 mStoppingActivities
for (int activityNdx = mStoppingActivities.size() - 1; activityNdx >= 0; --activityNdx) {
ActivityRecord s = mStoppingActivities.get(activityNdx);
......
}
return stops;
}
複製代碼
中間的詳細處理邏輯就不看了,咱們只須要關注這裏遍歷的是 ActivityStackSuperVisor 中的 mStoppingActivities 集合 。在前面分析 finish()
流程到最後的 addToStopping()
方法時提到過,
這些在等待銷燬的 Activity 被保存在了
ActivityStackSupervisor
的mStoppingActivities
集合中,它是一個ArrayList<ActivityRecord>
。
看到這裏,終於打通了流程。再回頭想一下文章開頭的例子,因爲人爲的在 SecondActivity 不間斷的向主線程塞消息,致使 Idler 遲遲沒法被執行,onStop/onDestroy
也就不會被回調。
對,不會被回調。 可實際狀況是這樣嗎?並非,明明是過了 10s 被回調。這就說明了即便主線程遲遲沒有機會執行 Idler,系統仍然提供了兜底機制,防止已經不須要的 Activity 長時間沒法被回收,從而形成內存泄漏等問題。從實際現象就能夠猜想到,這個兜底機制就是 onResume 以後 10s 主動去進行釋放操做。
再回到以前顯示待跳轉 Activity 的 ActivityStackSuperVisor.resumeFocusedStackTopActivityLocked()
方法。我這裏就不帶着你們追進去了,直接給出調用鏈。
ASS.resumeFocusedStackTopActivityLocked() -> ActivityStack.resumeTopActivityUncheckedLocked() -> ActivityStack.resumeTopActivityInnerLocked() -> ActivityRecord.completeResumeLocked() -> ASS.scheduleIdleTimeoutLocked()
> ActivityStackSuperVisor.java void scheduleIdleTimeoutLocked(ActivityRecord next) {
Message msg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG, next);
mHandler.sendMessageDelayed(msg, IDLE_TIMEOUT);
}
複製代碼
IDLE_TIMEOUT
的值是 10,這裏延遲 10s 發送了一個消息。這個消息是在 ActivityStackSupervisorHandler
中處理的。
private final class ActivityStackSupervisorHandler extends Handler {
......
case IDLE_TIMEOUT_MSG: {
activityIdleInternal((ActivityRecord) msg.obj, true /* processPausingActivities */);
} break;
......
}
void activityIdleInternal(ActivityRecord r, boolean processPausingActivities) {
synchronized (mService) {
activityIdleInternalLocked(r != null ? r.appToken : null, true /* fromTimeout */,
processPausingActivities, null);
}
}
複製代碼
忘記 activityIdleInternalLocked
方法的話能夠 ctrl+F 向上搜索一下。若是 10s 內主線程執行了 Idler 的話,就會移除這個消息。
到這裏,全部的問題就所有理清了。
說一些題外話,Android 面試進階指南 是我在小專欄維護的一個付費專欄,且已經有部分付費用戶。本文是第九篇文章了,爲了維護付費用戶的權益,沒有辦法把全部文章都同步過來。若是你對這個專欄感興趣,不妨 戳進來 看一看。
Activity 的 onStop/onDestroy 是依賴 IdleHandler 來回調的,正常狀況下當主線程空閒時會調用。可是因爲某些特殊場景下的問題,致使主線程遲遲沒法空閒,onStop/onDestroy 也會遲遲得不到調用。但這並不意味着 Activity 永遠得不到回收,系統提供了一個兜底機制,當 onResume 回調 10s 以後,若是仍然沒有獲得調用,會主動觸發。
雖然有兜底機制,但不管如何這確定不是咱們想看到的。若是咱們項目中的 onStop/onDestroy 延遲了 10s 調用,該如何排查問題呢?能夠利用 Looper.getMainLooper().setMessageLogging()
方法,打印出主線程消息隊列中的消息。每處理一條消息,都會打印以下內容:
logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what);
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
複製代碼
另外,因爲 onStop/onDestroy
調用時機的不肯定性,在作資源釋放等操做的時候,必定要考慮好,以免產生資源沒有及時釋放的狀況。