【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个主流程还是要执行的。

相关推荐
深海呐13 分钟前
Android AlertDialog圆角背景不生效的问题
android
ljl_jiaLiang14 分钟前
android10 系统定制:增加应用使用数据埋点,应用使用时长统计
android·系统定制
花花鱼15 分钟前
android 删除系统原有的debug.keystore,系统运行的时候,重新生成新的debug.keystore,来完成App的运行。
android
落落落sss2 小时前
sharding-jdbc分库分表
android·java·开发语言·数据库·servlet·oracle
消失的旧时光-19434 小时前
kotlin的密封类
android·开发语言·kotlin
服装学院的IT男5 小时前
【Android 13源码分析】WindowContainer窗口层级-4-Layer树
android
CCTV果冻爽6 小时前
Android 源码集成可卸载 APP
android
码农明明6 小时前
Android源码分析:从源头分析View事件的传递
android·操作系统·源码阅读
秋月霜风7 小时前
mariadb主从配置步骤
android·adb·mariadb
Python私教8 小时前
Python ORM 框架 SQLModel 快速入门教程
android·java·python