天天看点

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