最近發現項目裡的一個問題.從 Activity A 進入 Activity B.然後從B傳回的時候理論上應該是A onResume之後就會走B的onStop,onDestroy.但是并不是,發現在極端情況下會延遲将近10s.導緻有些資源沒有釋放(項目中是音視訊資源沒有釋放,導緻還在播放語音)帶着疑問我就去研究了finish的源代碼
public void finish() {
finish(DONT_FINISH_TASK_WITH_ACTIVITY);
}
複制
注意這個 DONT_FINISH_TASK_WITH_ACTIVITY 參數.這個函數最終會調用 ActivityManagerService.finishActivity
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)) {//走 else 分支
......
} else {
res = tr.getStack().requestFinishActivityLocked(token, resultCode,
resultData, "app-request", true);
}
return res;
} finally {
}
}
}
複制
finishTask = DONT_FINISH_TASK_WITH_ACTIVITY,是以走 requestFinishActivityLocked, finishActivityLocked.
這裡要先注意一下 pauseImmediately 的 boolean 值.從 finishActivityLocked 來看,應該是 false
final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData,
String reason, boolean oomAdj, boolean pauseImmediately) {
......
try {
// 設定r.finishing=true; 标記目前Activity為finishing
r.makeFinishingLocked();
......
if (mResumedActivity == r) {// 目前Activity即為活動Activity,進入if分支
......
r.setVisibility(false);
// 還未暫停目前Activity,是以mPausingActivity為null
if (mPausingActivity == null) {
// 開始暫停目前Activity
startPausingLocked(false, false, null, pauseImmediately);
}
} else if (r.state != ActivityState.PAUSING) {
// PAUSING結束,直接finish
......
} else {
// 正在 PAUSING,等它結束
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish waiting for pause of: " + r);
}
return false;
} finally {
}
}
複制
接下來看 startPausingLocked
final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,
ActivityRecord resuming, boolean pauseImmediately) {
......
ActivityRecord prev = mResumedActivity;
......
mResumedActivity = null;
mPausingActivity = prev;
......
// 設定目前Activity的狀态為pausing
prev.state = ActivityState.PAUSING;
......
if (prev.app != null && prev.app.thread != null) {
try {
// 在 ActivityThread 裡面
// 先走到 performPauseActivity.這裡面會有onPause的調用
// 然後調用 AMS 的 activityPaused
prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
userLeaving, prev.configChangeFlags, pauseImmediately);
} catch (Exception e) {
......
}
} else {
......
}
......
if (mPausingActivity != null) {// 前面已經指派
......
if (pauseImmediately) {// 前面已經提到,是 false
completePauseLocked(false, resuming);
return false;
} else {
// 向AMS主線程發送一個延時500ms的消息,執行completePauseLocked
schedulePauseTimeout(prev);
return true;
}
} else {
......
}
}
複制
這裡一共有兩個分支
- prev.app.thread.schedulePauseActivity
- schedulePauseTimeout(相對簡單,注釋裡已經有了)
schedulePauseActivity最終會走到AMS 的 activityPaused
final void activityPausedLocked(IBinder token, boolean timeout) {
final ActivityRecord r = isInStackLocked(token);
if (r != null) {
mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
if (mPausingActivity == r) {
......
try {
// 開始執行目前 Activity 暫停後的流程
// 參數 resumeNext 為 true,表示需要喚起下一個 Activity
completePauseLocked(true /* resumeNext */, null /* resumingActivity */);
} finally {
mService.mWindowManager.continueSurfaceLayout();
}
return;
} else {
......
}
}
}
複制
由此可見 startPausingLocked 最終都會走到 completePauseLocked
看一下 completePauseLocked
private void completePauseLocked(boolean resumeNext, ActivityRecord resuming) {
ActivityRecord prev = mPausingActivity;
if (prev != null) {
final boolean wasStopping = prev.state == STOPPING;
prev.state = ActivityState.PAUSED;
if (prev.finishing) {// 之前已經是 true
prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false);
} else if (prev.app != null) {
......
} else {
......
}
......
mPausingActivity = null;
}
if (resumeNext) {// 傳進來是 true
final ActivityStack topStack = mStackSupervisor.getFocusedStack();
if (!topStack.shouldSleepOrShutDownActivities()) {
// 代碼很多,主要還是調用下一個 Activity 的 onResume 方法
mStackSupervisor.resumeFocusedStackTopActivityLocked(topStack, prev, null);
} else {
......
}
}
......
}
複制
主要是以下兩個函數
- finishCurrentActivityLocked
- resumeFocusedStackTopActivityLocked
看一下第一個 finishCurrentActivityLocked.注意傳入的參數
final ActivityRecord finishCurrentActivityLocked(ActivityRecord r, int mode, boolean oomAdj) {
......
final ActivityRecord next = mStackSupervisor.topRunningActivityLocked();
if (mode == FINISH_AFTER_VISIBLE && (r.visible || r.nowVisible)
&& next != null && !next.nowVisible) {
if (!mStackSupervisor.mStoppingActivities.contains(r)) {
addToStopping(r, false /* scheduleIdle */, false /* idleDelayed */);
}
r.state = STOPPING;
return r;
}
......
}
複制
看一下 addToStopping.注意傳入的參數
void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed) {
if (!mStackSupervisor.mStoppingActivities.contains(r)) {
mStackSupervisor.mStoppingActivities.add(r);
}
boolean forceIdle = mStackSupervisor.mStoppingActivities.size() > MAX_STOPPING_TO_FORCE
|| (r.frontOfTask && mTaskHistory.size() <= 1);
// scheduleIdle = false, forceIdle = true
if (scheduleIdle || forceIdle) {
// idleDelayed = false
if (!idleDelayed) {
mStackSupervisor.scheduleIdleLocked();
} else {
// 延遲發送10s IDLE信号
mStackSupervisor.scheduleIdleTimeoutLocked(r);
}
} else {
checkReadyForSleep();
}
}
複制
由此可見 finishCurrentActivityLocked 就是延遲10s執行 activityIdleInternalLocked
接下來看第二個函數 resumeFocusedStackTopActivityLocked
boolean resumeFocusedStackTopActivityLocked() {
return resumeFocusedStackTopActivityLocked(null, null, null);
}
boolean resumeFocusedStackTopActivityLocked(
ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
if (!readyToResume()) {
return false;
}
if (targetStack != null && isFocusedStack(targetStack)) {
return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
}
final ActivityRecord r = mFocusedStack.topRunningActivityLocked();
if (r == null || r.state != RESUMED) {// 肯定走這裡
mFocusedStack.resumeTopActivityUncheckedLocked(null, null);
} else if (r.state == RESUMED) {
// Kick off any lingering app transitions form the MoveTaskToFront operation.
mFocusedStack.executeAppTransition(targetOptions);
}
return false;
}
複制
然後走 resumeTopActivityInnerLocked.裡面最重要的就是
try {
......
next.sleeping = false;
mService.showUnsupportedZoomDialogIfNeededLocked(next);
mService.showAskCompatModeDialogLocked(next);
next.app.pendingUiClean = true;
next.app.forceProcessStateUpTo(mService.mTopProcessState);
next.clearOptionsLocked();
next.app.thread.scheduleResumeActivity(next.appToken, next.app.repProcState,
mService.isNextTransitionForward(), resumeAnimOptions);
} catch (Exception e) {
......
}
複制
最終走這裡
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
ActivityClientRecord r = mActivities.get(token);
......
unscheduleGcIdler();//mGcIdlerScheduled=false,後面要用到
mSomeActivitiesChanged = true;
// 即将要 resume 的 activity 的參數調整
r = performResumeActivity(token, clearHide, reason);
if (r != null) {//一般不為 null
final Activity a = r.activity;
...... 讓整個 Activity 可見,并且調整 layout 布局
if (!r.onlyLocalRequest) {
r.nextIdle = mNewActivities;
mNewActivities = r;
// 看看 Idler 是啥
// 往主線程的消息隊列添加了一個空閑處理器,當主線程空閑時就會回調這個處理器來執行一些優先級較低的任務
Looper.myQueue().addIdleHandler(new Idler());
}
r.onlyLocalRequest = false;
if (reallyResume) {
try {
//正式onResume
ActivityManager.getService().activityResumed(token);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
} else {
......
}
}
複制
Idler 裡面會調用 ActivityManager.getService.activityIdle 方法.最終調用 activityIdleInternalLocked
final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
boolean processPausingActivities, Configuration config) {
......
// Stop any activities that are scheduled to do so but have been
// waiting for the next one to start.
for (int i = 0; i < NS; i++) {
r = stops.get(i);
final ActivityStack stack = r.getStack();
if (stack != null) {
if (r.finishing) {
//注意參數 FINISH_IMMEDIATELY
//注意是 stops 數組,調用onStop
stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false);
} else {
stack.stopActivityLocked(r);
}
}
}
for (int i = 0; i < NF; i++) {
r = finishes.get(i);
final ActivityStack stack = r.getStack();
if (stack != null) {
//finishes 數組,調用onDestroy
activityRemoved |= stack.destroyActivityLocked(r, true, "finish-idle");
}
}
......
return r;
}
複制
finish的流程基本總結如下
- (1)标記目前 Activity 為 finishing
- (2)設定目前 Activity 的狀态為 pausing (這裡正式開始pause)
- (3)調用目前 Activity 的 onPause 回調
- (4)completePauseLocked(不管是直接執行還是延遲500ms執行)(開始執行目前 Activity 暫停後的流程)
- (5)對即将 resume 的 Activity 進行 layout 調整,然後添加一個空閑處理器,最後正式 onResume
- 另外,延遲10s發送 IDLE 信号到處理器去強制 onStop,onDestroy
接下來就有個疑問了,為啥 onStop,onDestroy 沒有立即執行?原因就在這個空閑處理器
addIdleHandler 是在 MessageQueue 裡面的,也就是說的消息隊列.隻有一個地方調用了 Idler.queueIdle
Message next() {
......
for (;;) {
......
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
......
}
for (int i = 0; i < pendingIdleHandlerCount; i++) {
......
try {
keep = idler.queueIdle();//************************
} catch (Throwable t) {
}
......
}
......
}
}
複制
看到它調用的地方了沒?必須要等到 MessageQueue 裡面沒有資料的時候才會去調用
也就是說 onStop,onDestroy 的調用和消息隊列有關.如果消息隊列很快就空了, onStop,onDestroy 就很快傳回.如果主線程還有消息未處理(比如一直 invalidate )會一直處理,直到超過10s強制調用
現在終于明白為啥 onStop,onDestroy 沒有直接傳回.那麼文章最初的問題又該如何處理呢?很簡單,在onPause裡面用isFinishing方法來判斷,如果是true,就去取消音視訊,讓使用者無感覺(并沒有真正解決問題,如果要真正解決問題還是需要從主線程消息隊列來考慮)