【Android 13源码分析】Activity生命周期之onStop-2

忽然有一天,我想要做一件事:去代码中去验证那些曾经被"灌输"的理论。

-- 服装学院的IT男

整个流程分为3步:

    1. addToStopping 流程,将应用添加进需要stop的列表 -- system_service进程处理
    1. stopIfPossible 流程,也就是开始执行stop -- system_service进程处理
    1. 应用端处理stop流程 -- 应用进程处理

前面个都是system_service进程处理的,第三步是在 SourceActivity 的应用进程处理。

由于篇幅原因,分为以下2篇:

Activity生命周期之onStop-1

Activity生命周期之onStop-2

本篇为第二篇,介绍system_service进程是如何从 mStoppingActivities 中获取到 SourceActivity 并处理后续逻辑的。

1. 第二步:stopIfPossible 开始执行stop

根据上一小节后的2个线索,得到以下信息:

    1. "wm_stop_activity"的打印在 ActivityRecord::stopIfPossible 方法,并且打印log后就构建了 StopActivityItem
    1. 在 ActivityTaskSupervisor::activityIdleInternal 方法内有堆栈打印,日志如下:
ini 复制代码
ActivityTaskManager: activityIdleInternal:
Callers=com.android.server.wm.ActivityClientController.activityIdle:142
 android.app.IActivityClientController$Stub.onTransact:550
  com.android.server.wm.ActivityClientController.onTransact:125
   android.os.Binder.execTransactInternal:1285 

根据这2点信息其实就已经可以获得到这么一个完整的调用链了:

arduino 复制代码
ActivityClientController::activityIdle
    ActivityTaskSupervisor::activityIdleInternal
        ActivityTaskSupervisor::processStoppingAndFinishingActivities
            ActivityRecord::stopIfPossible
                ActivityRecord::setState  -- 设置为 STOPPING
                    StopActivityItem.obtain -- 构建触发StopActivityItem

后面的逻辑晚点再看,先分析 ActivityClientController::activityIdle 是怎么调用过来的。

1.1 ActivityClientController::activityIdle 的触发逻辑

TargetActivity 执行 onResume 的逻辑是在应用端是由 ActivityThread::handleResumeActivity方法开始的,而这个方法最后添加了一个 Idler 到 IdleHandler中,这个 Idler 就是触发 ActivityClientController::activityIdle 的地方。

整个AOSP中调用 ActivityClientController::activityIdle 的地方只有2个,根据log定位是在 ActivityThread 下的内部类 Idler 中。

arduino 复制代码
# ActivityThread
    public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
            boolean isForward, String reason) {
        ......
        // 触发 TargetActivity的 onResume
        if (!performResumeActivity(r, finalStateRequest, reason)) {
            return;
        }
        ......
        // 拿到activity
        final Activity a = r.activity;
        ......//setView 逻辑
        r.nextIdle = mNewActivities;
        mNewActivities = r;
        //触发SourceActivity 的onStop 
        if (localLOGV) Slog.v(TAG, "Scheduling idle handler for " + r);
        Looper.myQueue().addIdleHandler(new Idler());
    }

根据调用顺序:先触发 TargetActivity的 onResume,再触发 SourceActivity 的 onStop 。

这一点和实际的log打印也是对上了,接下来就看看 Idler() 是怎么执行的了。

addIdleHandler 方法会把一个 IdleHandler 添加到先洗队列的 mIdleHandlers 列表,这里是 Handler 知识点下关于 IdleHandler 的知识,不知道的可以搜一下相关的文章看看。 IdleHandler 的特效就是会在 CPU 空闲的时候才会执行相关任务。

1.1.1 Idler

Idler 是 ActivityThread 的内部类

java 复制代码
# ActivityThread
    private class Idler implements MessageQueue.IdleHandler {
        @Override
        public final boolean queueIdle() {
            ActivityClientRecord a = mNewActivities;
            ......
            if (a != null) {
                mNewActivities = null;
                final ActivityClient ac = ActivityClient.getInstance();
                ActivityClientRecord prev;
                do {
                    // 日志
                    if (localLOGV) Slog.v(
                        TAG, "Reporting idle of " + a +
                        " finished=" +
                        (a.activity != null && a.activity.mFinished));
                    if (a.activity != null && !a.activity.mFinished) {
                        
                        // 重点* 执行 ActivityClientController::activityIdle
                        ac.activityIdle(a.token, a.createdConfig, stopProfiling);
                        a.createdConfig = null;
                    }
                    prev = a;
                    a = a.nextIdle;
                    prev.nextIdle = null;
                } while (a != null);
            }
            if (stopProfiling) {
                mProfiler.stopProfiling();
            }
            return false;
        }
    }

这里的重点就是在主线程空闲的时候,就会执行 queueIdle() 方法,进而触发 SourceActivity 的 StopActivityItem 事务构建。 在日志中搜索添加 Idler 的日志和 queueIdle 方法执行的日志信息如下:

ini 复制代码
Line 12732: 04-01 20:50:30.891 24294 24294 V ActivityThread: Scheduling idle handler for ActivityRecord{b7ece46 token=android.os.BinderProxy@1fee9be {com.google.android.dialer/com.google.android.dialer.extensions.GoogleDialtactsActivity}}

Line 19074: 04-01 20:50:31.170 24294 24294 V ActivityThread: Reporting idle of ActivityRecord{b7ece46 token=android.os.BinderProxy@1fee9be {com.google.android.dialer/com.google.android.dialer.extensions.GoogleDialtactsActivity}} finished=false

可以看到时间上确实还是有很大间隔的。

1.2 ActivityClientController::activityIdle 的处理

上面看到了是如何调用过来的,现在看一下后续的调用逻辑

java 复制代码
# ActivityClientController
    @Override
    public void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                // Trace
                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityIdle");
                final ActivityRecord r = ActivityRecord.forTokenLocked(token);
                if (r == null) {
                    return;
                }
                // 重点逻辑
                mTaskSupervisor.activityIdleInternal(r, false /* fromTimeout */,
                        false /* processPausingActivities */, config);
                ......
            }
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
            Binder.restoreCallingIdentity(origId);
        }
    }

直接调用了 ActivityTaskSupervisor::activityIdleInternal 方法,这个方法在签名 SourceActivity 的 addToStopping 流程已经看过一次,现在条件不同执行的逻辑也不一样,再看一下这个方法。

scss 复制代码
# ActivityTaskSupervisor


        void activityIdleInternal(ActivityRecord r, boolean fromTimeout,
            boolean processPausingActivities, Configuration config) {
            // 重点* 1 log
            if (DEBUG_ALL) Slog.v(TAG, "Activity idle: " + r);
            // 重点* 2. ActivityRecord 不为null才执行内部逻辑
            if (r != null) {
                // 重点* 3. log
                if (DEBUG_IDLE) Slog.d(TAG_IDLE, "activityIdleInternal: Callers="
                        + Debug.getCallers(4));
                ......
            }
            ......
            // Atomically retrieve all of the other things to do. 原子检索所有其他要做的事情
            // 重点* 4. 看方法名是处理 停止和finish的Activity辑在这里
            processStoppingAndFinishingActivities(r, processPausingActivities, "idle");
            if (DEBUG_IDLE) {
                // 重点* 5. log 
                Slogf.i(TAG, "activityIdleInternal(): r=%s, mStartingUsers=%s", r, mStartingUsers);
            }
            ......
        }

这一次因为参数 r 不为null,所以还会打印"重点* 3"处的log,然后进入processStoppingAndFinishingActivities 方法。

1.2.1 再看 processStoppingAndFinishingActivities 逻辑(真正执行)

java 复制代码
# ActivityTaskSupervisor

    /**
     * Processes the activities to be stopped or destroyed. This should be called when the resumed
     * 处理要停止或销毁的Activity。这应该在resumed时调用
     * activities are idle or drawn.
     */
    private void processStoppingAndFinishingActivities(ActivityRecord launchedActivity,
            boolean processPausingActivities, String reason) {
                // 准备要执行 Stop 的Activity 集合 
                ArrayList<ActivityRecord> readyToStopActivities = null;
                // 重点 * 1. 遍历mStoppingActivities
                for (int i = mStoppingActivities.size() - 1; i >= 0; --i) {
                    // 获取到ActivityRecord (当前分析场景就1个)
                    final ActivityRecord s = mStoppingActivities.get(i);
                    final boolean animating = s.isAnimating(TRANSITION | PARENTS,
                            ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)
                            || s.inTransition();
                    // 日志
                    ProtoLog.v(WM_DEBUG_STATES, "Stopping %s: nowVisible=%b animating=%b "
                            + "finishing=%s", s, s.nowVisible, animating, s.finishing);
                    // 条件满足才执行
                    if (!animating || mService.mShuttingDown) {
                        ......
                        // 打印 准备stop的log
                        ProtoLog.v(WM_DEBUG_STATES, "Ready to stop: %s", s);
                        if (readyToStopActivities == null) {
                            readyToStopActivities = new ArrayList<>();
                        }
                        // 重点 * 2. 添加进集合
                        readyToStopActivities.add(s);
                        // 从集合中移除
                        mStoppingActivities.remove(i);
                    }
                }
                // 重点 * 3. 遍历readyToStopActivities
                final int numReadyStops = readyToStopActivities == null ? 0 : readyToStopActivities.size();
                for (int i = 0; i < numReadyStops; i++) {
                    final ActivityRecord r = readyToStopActivities.get(i);
                    // 检查该ActivityRecord对象是否在历史记录中。  
                    if (r.isInHistory()) {
                        // 如果该ActivityRecord对象正在结束(可能是用户或系统触发的结束操作)。
                        if (r.finishing) {
                            // TODO(b/137329632): Wait for idle of the right activity, not just any.
                            r.destroyIfPossible(reason);
                        } else {
                            // 重点* 4. 如果ActivityRecord对象不在结束状态,则尝试停止它
                            r.stopIfPossible();
                        }
                    }
                }
                ......
            }

这个时候再看这个方法就能理解方法上面的注释了:This should be called when the resumed 。

    1. 遍历 mStoppingActivities 集合,这个集合里是有当前场景的唯一元素是 SourceActivity (当前是QuickstepLauncher)。

这一次的打印如下:

ini 复制代码
WindowManager: Stopping ActivityRecord{f40797d u0 com.android.launcher3/.uioverrides.QuickstepLauncher} t20}: nowVisible=false animating=false finishing=false  mShuttingDown=false
    1. 由于"animating=false"所以会执行 if 内部语句,把 SourceActivity 添加到 readyToStopActivities 集合中。

并打印ProtoLog

vbnet 复制代码
WindowManager: Ready to stop: ActivityRecord{f40797d u0 com.android.launcher3/.uioverrides.QuickstepLauncher} t20}
    1. 遍历 readyToStopActivities 下的元素,然后执行 ActivityRecord::stopIfPossible 方法

processStoppingAndFinishingActivities 方法是处理 Activity 的核心方法,他的触发点当前文章就分析到了2个,另外肯定还有其他的调用逻辑 比如我将 ActivityThread::handleResumeActivity 方法下最后一行将 Idler 添加进 IdleHandler 的代码删了,本以为就不会执行 SourceActivity 的 onStop 了,没想到还会通过发送一个 PROCESS_STOPPING_AND_FINISHING_MSG 消息来触发 processStoppingAndFinishingActivities 方法的执行。 毕竟 Activity 生命周期是基础代码,google这快代码的健壮性肯定是考虑周全的。

1.3 真正触发StopActivityItem构建的地方 ActivityRecord::stopIfPossible

scss 复制代码
# ActivityRecord
    void stopIfPossible() {
        // log
        if (DEBUG_SWITCH) Slog.d(TAG_SWITCH, "Stopping: " + this);
        final Task rootTask = getRootTask();
        ......
        try {
            stopped = false;
            // 关键log (这里还是即将 stop)
            ProtoLog.v(WM_DEBUG_STATES, "Moving to STOPPING: %s (stop requested)", this);
            ......
            // 重点* 1. 设置Activity的状态为STOPPING,原因为"stopIfPossible"
            setState(STOPPING, "stopIfPossible");

            if (DEBUG_VISIBILITY) {
                // log
                Slog.v(TAG_VISIBILITY, "Stopping:" + this);
            }
            // events日志 :wm_stop_activity: [0,198530468,com.example.myapplication/.MainActivity]
            EventLogTags.writeWmStopActivity(
                    mUserId, System.identityHashCode(this), shortComponentName);
            // 重点* 2. 构建执行 Stop 事务
            mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token,
                    StopActivityItem.obtain(configChangeFlags));

            mAtmService.mH.postDelayed(mStopTimeoutRunnable, STOP_TIMEOUT);
        }
    }

就做了2件事:

    1. 设置状态为 STOPPING
    1. 构建执行 StopActivityItem

不过这里有很多log,跟踪问题的时候可以关注一下。

2. SystemService 端小结

当前冷启动场景下,SourceActivity 的onStop处理,SystemService 端其实就做了2件事。

    1. 把 SourceActivity 添加到 ActivityTaskSupervisor 类下的 mStoppingActivities 集合中,这一步我成为"addToStopping 流程",对应的events日志为:"wm_add_to_stopping" 这一步的执行也毕竟早,在 SourceActivity 执行完 Pause 流程就触发了。
    1. 执行 ActivityTaskSupervisor::processStoppingAndFinishingActivities 方法,将 mStoppingActivities 集合下的 ActivityRecord 取出,执行 ActivityRecord::stopIfPossible 方法,在这里构建执行 StopActivityItem 。

当前分析的是常见流程的调用逻辑,还有一些其他逻辑也会触发onStop,但是目前看来都会执行 ActivityTaskSupervisor::processStoppingAndFinishingActivities 方法,所以以后遇到相关问题可以关注一下该方法的执行。

到这里 SystemService 的处理就结束了,后续流程就在应用端了。

那后续就是应用进程执行 Stop 的流程了。

3.应用端 onStop 处理

应用端的处理就相对简单了,先看一下 StopActivityItem 的定义

typescript 复制代码
# StopActivityItem

    @Override
    public void execute(ClientTransactionHandler client, ActivityClientRecord r,
            PendingTransactionActions pendingActions) {
        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
        //  触发应用进程stop
        client.handleStopActivity(r, mConfigChanges, pendingActions,
                true /* finalStateRequest */, "STOP_ACTIVITY_ITEM");
        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
    }

具体的实现肯定是在ActivityThread中了,整理的调用链如下:

arduino 复制代码
StopActivityItem::execute
    ClientTransactionHandler::handleStopActivity
        ActivityThread::handleStopActivity
            ActivityThread::performStopActivityInner
                ActivityThread::callActivityOnStop
                    ActivityThread::callActivityOnSaveInstanceState  -- OnSaveInstanceState 逻辑
                    Activity::performStop
                        Instrumentation.callActivityOnStop
                            Activity::onStop        -- 生命周期
            ActivityThread::updateVisibility        -- 处理View不可见

开始撸代码

java 复制代码
# ActivityThread

    @Override
    public void handleStopActivity(ActivityClientRecord r, int configChanges,
            PendingTransactionActions pendingActions, boolean finalStateRequest, String reason) {
        r.activity.mConfigChangeFlags |= configChanges;

        final StopInfo stopInfo = new StopInfo();
        // 重点* 1. onStop 流程
        performStopActivityInner(r, stopInfo, true /* saveState */, finalStateRequest,
                reason);

        if (localLOGV) Slog.v(
            TAG, "Finishing stop of " + r + ": win=" + r.window);
        // 重点* 2. 更新Activity的可见性状态为不可见
        updateVisibility(r, false);
        ......
    }

这里分为2步逻辑,先执行 onStop ,再处理 View 可见性。

3.1 onStop 执行逻辑

typescript 复制代码
# ActivityThread

    private void performStopActivityInner(ActivityClientRecord r, StopInfo info,
            boolean saveState, boolean finalStateRequest, String reason) {
            ......
            // One must first be paused before stopped... 
            // 1. 必须先执行 pause,才可以 stop。
            performPauseActivityIfNeeded(r, reason);
            ......
            // 2. 主流程
            callActivityOnStop(r, saveState, reason);
        }

这个方法做了2件事:

    1. 必须先执行 pause,才可以 stop,所以需要执行一下 performPauseActivityIfNeeded 方法,不过正常逻辑执行到这,已经是pause状态了,所以这个方法内部进去就会return。
typescript 复制代码
# ActivityThread
    private void performPauseActivityIfNeeded(ActivityClientRecord r, String reason) {
        if (r.paused) {
            // You are already paused silly...
            return;
        }
        ......
    }
    1. 继续执行主流程,触发Activity的 onStop 回调
scss 复制代码
# ActivityThread

    private void callActivityOnStop(ActivityClientRecord r, boolean saveState, String reason) {
        // Before P onSaveInstanceState was called before onStop, starting with P it's
        // called after. Before Honeycomb state was always saved before onPause.
        final boolean shouldSaveState = saveState && !r.activity.mFinished && r.state == null
                && !r.isPreHoneycomb();
        final boolean isPreP = r.isPreP();
        if (shouldSaveState && isPreP) {
            // 重点* 1. callActivityOnSaveInstanceState
            callActivityOnSaveInstanceState(r);
        }
        try {
            // 重点* 2. onStop流程
            r.activity.performStop(r.mPreserveWindow, reason);
        } ......

        if (shouldSaveState && !isPreP) {
            // 重点* 1. callActivityOnSaveInstanceState
            callActivityOnSaveInstanceState(r);
        }
    }
    1. 可以看到2次执行了 onSaveInstanceState 回调,这个可以在【Android 11源码分析】看相关的
    1. 是本次分析的主流程,已经执行到Activity的方法了,里生命周期onStop不远了
arduino 复制代码
# Activity
    private Instrumentation mInstrumentation;

    final void performStop(boolean preserveWindow, String reason) {
        ......
        // Fragment的处理
        mFragments.dispatchStop();
        mCalled = false;
        // 回调onStop流程
        mInstrumentation.callActivityOnStop(this);
        ......
    }

Instrumentation 这个类中处理了所有的Activity生命周期回调。

csharp 复制代码
# Instrumentation

    public void callActivityOnStop(Activity activity) {
        // onStop
        activity.onStop();
    }

3.2 Stop 处理View的可见性

ini 复制代码
# ActivityThread
    private void updateVisibility(ActivityClientRecord r, boolean show) {
        // 拿到对应Activity的 DecorView
        View v = r.activity.mDecor;
        if (v != null) {
            if (show) {
                // 如果Activity当前在服务器端不可见, 就需要处理成可见
                if (!r.activity.mVisibleFromServer) {
                    r.activity.mVisibleFromServer = true;
                    mNumVisibleActivities++;
                    if (r.activity.mVisibleFromClient) {
                        // 设置可见
                        r.activity.makeVisible();
                    }
                }
            } else {
                // 如果Activity当前在服务器端可见, 就需要处理成不可见
                if (r.activity.mVisibleFromServer) {
                    r.activity.mVisibleFromServer = false;
                    mNumVisibleActivities--;
                    // 影藏View
                    v.setVisibility(View.INVISIBLE);
                }
            }
        }
    }

所以经常会有人执行了onStop 才是真正的Activity不可见,原因就是onStop的逻辑会把 View设置为不可见,间接的Activity也就不可见了。

4. 总结

当前只是以桌面冷启动应用的场景来分析桌面的 onStop 流程,整个流程分析完对 onStop 流程也有了一个比较完整的了解,但是当前分析的调用流程并不能代表所有场景。具体情况还是需要具体分析,比如应用内启动 Activity 的调用链肯定和当前是有别的,但是无论怎么样,2个主流程还是要执行的。

相关推荐
调皮的芋头20 分钟前
iOS各个证书生成细节
人工智能·ios·app·aigc
太空漫步111 小时前
android社畜模拟器
android
海绵宝宝_4 小时前
【HarmonyOS NEXT】获取正式应用签名证书的签名信息
android·前端·华为·harmonyos·鸿蒙·鸿蒙应用开发
凯文的内存6 小时前
android 定制mtp连接外设的设备名称
android·media·mtp·mtpserver
天若子6 小时前
Android今日头条的屏幕适配方案
android
林的快手7 小时前
伪类选择器
android·前端·css·chrome·ajax·html·json
望佑8 小时前
Tmp detached view should be removed from RecyclerView before it can be recycled
android
xvch10 小时前
Kotlin 2.1.0 入门教程(二十四)泛型、泛型约束、绝对非空类型、下划线运算符
android·kotlin
人民的石头14 小时前
Android系统开发 给system/app传包报错
android
yujunlong391914 小时前
android,flutter 混合开发,通信,传参
android·flutter·混合开发·enginegroup