美文网首页
Activity onStop,onDestroy延迟10s执行

Activity onStop,onDestroy延迟10s执行

作者: 勇敢地追 | 来源:发表于2020-09-08 09:27 被阅读0次

    最近发现项目里的一个问题.从 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,就去取消音视频,让用户无感知(并没有真正解决问题,如果要真正解决问题还是需要从主线程消息队列来考虑)

    相关文章

      网友评论

          本文标题:Activity onStop,onDestroy延迟10s执行

          本文链接:https://www.haomeiwen.com/subject/aczkektx.html