天天看點

Activity onStop,onDestroy延遲10s執行

最近發現項目裡的一個問題.從 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,就去取消音視訊,讓使用者無感覺(并沒有真正解決問題,如果要真正解決問題還是需要從主線程消息隊列來考慮)