当前位置: 首页 > article >正文

【Android 14源码分析】Activity启动流程-2

忽然有一天,我想要做一件事:去代码中去验证那些曾经被“灌输”的理论。
                  
                  
                                           – 服装学院的IT男

本篇已收录于Activity短暂的一生系列
欢迎一起学习讨论Android应用开发或者WMS
V:WJB6995
Q:707409815

正文

由于篇幅原因,整个启动流程分为以下3篇进行分析:

Activity启动流程-1

Activity启动流程-2

Activity启动流程-3

继续上一篇执行到 TaskFragment::resumeTopActivity 分析。
当前分析进度还处于阶段一的逻辑,阶段一最后是需要触发2个逻辑:

    1. 触发 SourceActivity 执行 pause
    1. 触发创建 TargetActivity 所在进程

1. 阶段一 : system_service 处理

1.1 显示顶层 Activity–TaskFragment::resumeTopActivity

TaskFragment::resumeTopActivity 方法是 Framework 中非常常见的一个方法,方法名和它的功能是一样的:显示顶层 Activity 。

抛开多窗口场景,设备上显示 Activity 的逻辑就是显示 DefaultTaskDisplayArea 下的顶层 Task 中的顶层 Activity ,代码流程有很多场景都可能会导致屏幕显示内容的修改,也就需要执行当前方法来确保屏幕上有一个正确的 Activity 显示。

以后会经常看到这个方法,当前还是只分析 Launcher 启动“电话”的场景来看看执行逻辑。

# TaskFragment

    final boolean resumeTopActivity(ActivityRecord prev, ActivityOptions options,
            boolean deferPause) {
                // 这里的next返回的是电话的 MainActivity,表示下一个需要显示的Activity
                ActivityRecord next = topRunningActivity(true /* focusableOnly */);
                ......
                // 如果跳过的这些逻辑都没执行return,则正在开始执行 resume 流程,打印关键日志。    
                // 前面流程如果返回也有日志的打印
                // 打印日志,需要显示哪个Activity
                if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);
                ......
                // 重点* 1. 这里会将 Launcher 的Activity pause 。参数是电话的 ActivityRecord
                // 注意 1:如果有 Activity 被 pauseBackTasks 方法触发了 pause,则返回 true,表示有 Activity 正在 pausing
                boolean pausing = !deferPause && taskDisplayArea.pauseBackTasks(next);
                ......
                if (pausing) {
                    ProtoLog.v(WM_DEBUG_STATES, "resumeTopActivity: Skip resume: need to"+ " start pausing");
                    if (next.attachedToProcess()) {
                        ......
                    } else if (!next.isProcessRunning()) {
                        // 重点*2. 进程没有运行,则触发异步创建进程。 当前逻辑肯定是执行这一条
                        final boolean isTop = this == taskDisplayArea.getFocusedRootTask();
                        mAtmService.startProcessAsync(next, false /* knownToBeDead */, isTop,
                            isTop ? HostingRecord.HOSTING_TYPE_NEXT_TOP_ACTIVITY
                                    : HostingRecord.HOSTING_TYPE_NEXT_ACTIVITY);
                    }
                    ......
                    // 注意2:这里会return
                    return true;
                } else if ......
                ......//后面还有重要逻辑,当前可忽略   
            }

上面说过这个函数非常复杂,在当前逻辑有2个主线:

    1. pause 当前 Activity 也就是 Launcher
    1. 异步创建“电话”的进程

在第一步将 Launcher 的 Activity 执行 pause ,这一步执行到最后也会触发"电话"应用 MainActivity 的启动。
第二步创建“电话”进程,进程创建完肯定也会执行"电话"应用 MainActivity 的启动,这么看来就有2个地方触发了。

这是因为是异步创建进程,不知道谁先执行完,但是可以明确的是,"电话"应用 MainActivity 必须有2个条件:

    1. 前一个Activity执行完pause
    1. 进程创建完成

所以无论2个分支哪一个先执行完,都需要等后一个执行完的来触发后续电话"应用 MainActivity 的启动。
这个方法还有2个需要注意的地方:

    1. 变量 pausing 表示当前是否正在执行 Activity 的 pause 流程。这个值受2个因素影响,

先看 Launcher 的 pause 流程。

1.2 pause 流程–pauseBackTasks

需要显示新的 Activity 那之前的 Activity 肯定是要执行 pause ,参数 next 为“电话”的 ActivityRecord 。
这一步主要是触发 SourceActivity 的pause 逻辑。

# TaskDisplayArea

    // 可以看到“电话”ActivityRecord在这里就被称为resuming
    boolean pauseBackTasks(ActivityRecord resuming) {
        final int[] someActivityPaused = {0};
        forAllLeafTasks(leafTask -> {
            // Check if the direct child resumed activity in the leaf task needed to be paused if
            // the leaf task is not a leaf task fragment.
            if (!leafTask.isLeafTaskFragment()) {
                // 当前不会走这里
                ......
            }

            leafTask.forAllLeafTaskFragments((taskFrag) -> {
                final ActivityRecord resumedActivity = taskFrag.getResumedActivity();
                if (resumedActivity != null && !taskFrag.canBeResumed(resuming)) {
                    if (taskFrag.startPausing(false /* uiSleeping*/, resuming, "pauseBackTasks")) {
                        someActivityPaused[0]++;
                    }
                }
            }, true /* traverseTopToBottom */);
        }, true /* traverseTopToBottom */);
        return someActivityPaused[0] > 0;
    }

forAllLeafTasks 和 forAllLeafTaskFragments 在WMS/AMS 常见方法调用提取有解释,那么当前这段方法其实就是让 DefaultTaskDisplayArea 下的每个叶子 LeafTaskFragments 执行 startPausing 。

# TaskFragment

    boolean startPausing(boolean userLeaving, boolean uiSleeping, ActivityRecord resuming,
            String reason) {
        ......
        // 日志输出当前 TaskFragment 和 mResumedActivity 的关系。后面会贴上日志证明
        // 注意这里的是需要 pause 的,不是需要 resume 的
        ProtoLog.d(WM_DEBUG_STATES, "startPausing: taskFrag =%s " + "mResumedActivity=%s", this,
                mResumedActivity);
        ......
        // 后面的prev就是launcher的ActivityRecord了
        ActivityRecord prev = mResumedActivity;
        ......
        // 输出日志
        ProtoLog.v(WM_DEBUG_STATES, "Moving to PAUSING: %s", prev);
        mPausingActivity = prev;
        ......
        // 设置window状态为PAUSING
        prev.setState(PAUSING, "startPausingLocked");
        prev.getTask().touchActiveTime();
        ......
        if (prev.attachedToProcess()) {
            // launcher的进程肯定是满足启动条件的
            if (shouldAutoPip) {
                // 当前场景与画中画模式无关,不走这
                boolean didAutoPip = mAtmService.enterPictureInPictureMode(
                        prev, prev.pictureInPictureArgs);
                ProtoLog.d(WM_DEBUG_STATES, "Auto-PIP allowed, entering PIP mode "
                        + "directly: %s, didAutoPip: %b", prev, didAutoPip);
            } else {
                // 重点*1. 上面的PIP日志没输出,肯定走的是这
                schedulePauseActivity(prev, userLeaving, pauseImmediately,
                        false /* autoEnteringPip */, reason);
            }
        }
        ......
    }

    void schedulePauseActivity(ActivityRecord prev, boolean userLeaving,
            boolean pauseImmediately, boolean autoEnteringPip, String reason) {
        // 输出日志
        ProtoLog.v(WM_DEBUG_STATES, "Enqueueing pending pause: %s", prev);
        try {
            // 输出events 日志
            EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev),
                    prev.shortComponentName, "userLeaving=" + userLeaving, reason);
            // 重点* 构建并执行PauseActivityItem
            mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
                    prev.token, PauseActivityItem.obtain(prev.finishing, userLeaving,
                            prev.configChangeFlags, pauseImmediately, autoEnteringPip));
        } catch (Exception e) {
            ......
        }
    }

最终在 TaskFragment::schedulePauseActivity 构建 PauseActivityItem 并执行了 Launcher 的 pause 事件。

这块相关的日志输入如图:

在这里插入图片描述

因为是后面补充的所以对象不一样,但是能确定这里处理的 TaskFragment 和 mResumedActivity 都是 Launcher 对象的.

State movement 这段的输出是在 ActivityRecord::setState 只要状态改变都会输出

1.2.1 PauseActivityItem

触发 pause 流程就是通过 PauseActivityItem 这个触发的。

# PauseActivityItem

    @Override
    public void execute(ClientTransactionHandler client, ActivityClientRecord r,
            PendingTransactionActions pendingActions) {
        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
        // 重点*1. 触发 handlePauseActivity 流程
        client.handlePauseActivity(r, mFinished, mUserLeaving, mConfigChanges, pendingActions,
                "PAUSE_ACTIVITY_ITEM");
        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
    }

    @Override
    public int getTargetState() {
        return ON_PAUSE;
    }
    //  pauser执行后调用 postExecute
    @Override
    public void postExecute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
        if (mDontReport) {
            return;
        }
        //  重点*2. 触发启动新的Activity
        ActivityClient.getInstance().activityPaused(token);
    }

这里先执行 execute 方法也就是 Launcher 的 pause 流程,这个不是主线。

然后执行 postExecute,这里会触发新 Activity 的启动,这个流程就是阶段二。

# ActivityClient

    public void activityPaused(IBinder token) {
        try {
            getActivityClientController().activityPaused(token);
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
        }
    }

1.3 启动进程

这一步就是 TaskFragment::resumeTopActivity 触发的 TargetActivity 所在的进程创建,这一步的后续逻辑是阶段三。

2. 第一阶段总结

本篇对整个 Activity 启动流程做了介绍,然后重点分析第一阶段的事情,这一阶段的二级框图如下:

在这里插入图片描述

    1. 在 Launcher 进程构建了启动参数放在了 ActivityOption 中,然后通过 Bundle 传递给 system_service 端
    1. AMS 先解析参数,放在了 Request 这个类中保存
    1. AMS 构建出一个 ActivityRecord ,这个类在 system_service 端就代表着 Activity ,同时也是一个窗口容器
    1. 再构建出一个 Task 挂载到窗口树上
    1. 将 ActivityRecord 挂载到 Task 中,这样 ActivityRecord 也就挂载到窗口层级树中了
    1. 触发 Launcher 执行 pause 逻辑,也就是阶段二
    1. 触发 TargetActivity 所在的进程创建,也就是阶段三

这里再补充 system_service 处理的完整堆栈:

ActivityTaskManagerService::startActivity
    ActivityTaskManagerService::startActivityAsUser
        ActivityTaskManagerService::startActivityAsUser
            ActivityStartController::obtainStarter
                ActivityStarter::execute
                    ActivityStarter$Request::resolveActivity           -- 解析启动请求参数
                        ActivityTaskSupervisor::resolveIntent
                        ActivityTaskSupervisor::resolveActivity
                    ActivityStarter::executeRequest                    -- 3.3 创建ActivityRecord
                        ActivityStarter::startActivityUnchecked
                            ActivityStarter::startActivityInner        -- 3.4 关键函数startActivityInner
                                ActivityStarter::getOrCreateRootTask   -- 3.4.1 创建或者拿到Task
                                    RootWindowContainer::getOrCreateRootTask
                                        RootWindowContainer::getOrCreateRootTask
                                            TaskDisplayArea::getOrCreateRootTask
                                                TaskDisplayArea::getOrCreateRootTask
                                                    Task::Build     ---创建Task                    
                                ActivityStarter::setNewTask            -- 3.4.2 将Task与activityRecord 绑定
                                    ActivityStarer::addOrReparentStartingActivity
                                Task::moveToFront                       --3.4.3 移动Task到栈顶
                                    Task::moveToFrontInner
                                        TaskDisplayArea::positionChildAt
                                            TaskDisplayArea::positionChildTaskAt
                                                ActivityTaskSupervisor::updateTopResumedActivityIfNeeded
                                                    ActivityRecord::onTopResumedActivityChanged      --触发TopResumedActivityChangeItem
                                RootWindowContainer::resumeFocusedTasksTopActivities    --3.4.4 显示Activity
                                    Task::resumeTopActivityUncheckedLocked
                                        Task::resumeTopActivityInnerLocked
                                            TaskFragment::resumeTopActivity      -- 1. 显示顶层Activity
                                                TaskDisplayArea::pauseBackTasks  -- 1.2 pause LauncherActivity 
                                                    WindowContainer::forAllLeafTask
                                                        TaskFragment::forAllLeafTaskFragments
                                                            TaskFragment::startPausing
                                                                TaskFragment::startPausing
                                                                    TaskFragment::schedulePauseActivity --构建 PauseActivityItem,这里是触发暂停launch
                                                ActivityTaskManagerService::startProcessAsync     -- 1.3 创建“电话”进程

2.1 容器变化

之前看过窗口层级树在冷启动前面的对比:

在这里插入图片描述
其中 WindowState 部分和其他 Task 和启动流程无关,先不管。

在桌面的时候 DefaultTaskDisplayArea 下只有 Launcher 所在的 Task 有元素,如下图:

在这里插入图片描述

  • 忽略掉其他的空 Task 所有 DefaultTaskDisplayArea 下的顶层 Task 就是 Launcher 所在的 Task 1
  • Task 1 下是一个 Task 8 然后内部才是 Launcher 的 ActivityRecord ,这个 ActivityRecord 目前也是整个手机系统里的顶层 Activity
  • 这个时候用户角度看到的 Activity 也是 Launcher 的 Activity

在阶段一处理完后,主要操作集中在 ActivityStarter::startActivityInner 方法对窗口容器的操作,操作完后如下图:

在这里插入图片描述
经过阶段一的处理,已经创建了 TargetActivity 所在的 Task 和对应的 ActivityRecord ,并也挂载到 DefaultTaskDisplayArea 下了,而且还将其移到了顶部,所以得到上面主要的一个关系。
但是这个时候用户还是只能看到 Launcher 的 Activity 。这是因为 TargetActivity 都还没启动,甚至连它的进程这会都没创建。

阶段一 DefaultTaskDisplayArea 下结构已经如图了,为了能让用户的实际视觉也这样,所以要让 TargetActivity 启动起来,所以触发了 RootWindowContainer::resumeFocusedTasksTopActivities 方法,但是在执行到 TaskFragment::resumeTopActivity 方法的时候发现想显示 TargetActivity 的条件2个都没满足,所以就需要触发 Launcher 的pause 和 TargetActivity 所在进程的创建。也就是后面要看的阶段 二,三 。

2.1 后续阶段预览

按照之前说的整个启动流程分为4个阶段,目前已经分析完了第一阶段。也知道为啥要执行二,三阶段了,后面要分析的二,三阶段由于提过多次是异步的,取决执行速度,这个是不确定的。也是因为这个原因所以网上有很多博客关于 Activity 启动流程的分析都不太一样,其实也许都没有错,只是各自分析的具体执行顺序不同。

经过二,三阶段后,就要触发应用进程启动 TargetActivity 了,第一阶段和第四阶段的流程是固定的,第二,三阶段还是有点不确定性的。

既然从触发顺序上是先执行 pause 再触发进程创建,那么后面的逻辑就以先完成 completePause 再完成进程创建的执行循序分析,当然在后面的分析中,一些关键的分歧点也会着重标记,尽量让读者能区分各个场景的执行逻辑。

3. 阶段二–completePause

3.1 流程概述

应用完成 Pause 会执行 ActivityClientController::activityPaused 来通知 system_service 当前分析主流程继续看 completePause 的后续逻辑,这部分调用链如下:

调用堆栈如下:

在这里插入图片描述

ActivityClientController::activityPaused
    ActivityRecord::activityPaused
        TaskFragment::completePause
            RootWindowContainer::resumeFocusedTasksTopActivities       -- 显示顶层Activity
                RootWindowContainer::resumeFocusedTasksTopActivities
                    Task::resumeTopActivityUncheckedLocked
                        Task::resumeTopActivityInnerLocked
                            TaskFragment::resumeTopActivity
                                ActivityTaskSupervisor::startSpecificActivity        -- 试图启动 Activity
            RootWindowContainer::ensureActivitiesVisible              -- 确保设备上 Activity 的可见性
                RootWindowContainer::ensureActivitiesVisible
                    DisplayContent::ensureActivitiesVisible 
                        WindowContainer::forAllRootTasks  --忽略固定逻辑
                            Task::ensureActivitiesVisible
                                Task::forAllLeafTasks --忽略固定逻辑
                                    TaskFragment::updateActivityVisibilities
                                        EnsureActivitiesVisibleHelper::process
                                            EnsureActivitiesVisibleHelper::setActivityVisibilityState
                                                EnsureActivitiesVisibleHelper::makeVisibleAndRestartIfNeeded
                                                    ActivityTaskSupervisor::startSpecificActivity       -- 试图启动 Activity

对应的时序图如下:

在这里插入图片描述
根据堆栈信息发现有2个地方都会触发 ActivityTaskSupervisor::startSpecificActivity 。

    1. resumeFocusedTasksTopActivities 流程。这个方法在阶段一看过,目的就是要显示一个顶层 Activity,但是第一阶段因为目标进程没起来,SourceActivity 也没 pause 所以最终也没触发 TargetActivity 的启动。
    1. ensureActivitiesVisible 流程。这个流程执行的概率很高,界面有个风吹草动可能影响 Activity 显示的都会执行这个方法,这个方法的目的就是确保当前设备上的 Activity 正确显示。
      比如当前这个场景,一个 Activity 完成了 completePause ,那肯定要有新的 Activity 显示,那整个手机系统的 Activity 规则肯定发生了改变,所以需要执行 ensureActivitiesVisible 流程来确保正确的显示。

注意:ensureActivitiesVisible 流程并不是针对1个 Activity 而是整个设备上所有的 Activity ,让他们该显示显示,该隐藏隐藏。

之前看了第一阶段的窗口图:

在这里插入图片描述
冷启动的目标肯定是希望最后显示的是 TargetActivity ,也就是下面这图的状态:

在这里插入图片描述
其实在 system_service 这边的 DefaultTaskDisplayArea 并没有变化,只是用户看到的 Activity 变成了 TargetActivity 。

要实现这个最终状态就是需要目标应用端创建 TargetActivity 。而要触发这一步骤是2个条件就是:

    1. 其他 Activity 完成 pause
    1. TargetActivity 进程在运行

所以现在看到的阶段二就一直在试图来启动 TargetActivity ,这一阶段的大概流程图如下:

在这里插入图片描述
可以看到对应代码 TaskFragment::completePause 后有2个流程都试图执行 ActivityTaskSupervisor::startSpecificActivity 来启动 TargetActivity。

  • 如果在 resumeFocusedTasksTopActivities 流程的时候进程启动了,则会走完触发 TargetActivity 的启动逻辑。如果进程还未启动,则会在 startSpecificActivity 方法里触发一下创建进程

  • resumeFocusedTasksTopActivities 流程执行完后会执行 ensureActivitiesVisible 流程来遍历整个手机系统的所有 Activity 处理他们的可见性,如果这个时候发现顶层 Activity 的ActivityRecord::attachedToProcess 返回false,则又会执行到 startSpecificActivity 。否则会根据具体情况处理一下其可见性。

这里其实还有一个可能的逻辑,那就是在 resumeFocusedTasksTopActivities 流程下进程没创建好,但是在走的 ensureActivitiesVisible 的时候进程创建好了,并触发了启动TargetActivity 的启动逻辑,那这个时候 ensureActivitiesVisible 则就“处理一下其可见性”。

有个注意点,这个图里提到的ActivityRecord::attachedToProcess 方法的返回值,并不严格等于“进程是否创建”,详细区别在 3.1.2 小节分析

理论知识建设完毕,下面来说跟代码,从 ActivityRecord::activityPaused 方法开始。

# ActivityRecord
    void activityPaused(boolean timeout) {
        ......
            if (pausingActivity == this) {
                // 打印日志
                ProtoLog.v(WM_DEBUG_STATES, "Moving to PAUSED: %s %s", this,
                        (timeout ? "(due to timeout)" : " (pause complete)"));
                mAtmService.deferWindowLayout();
                try {
                    // 注意2个产出
                    taskFragment.completePause(true /* resumeNext */, null /* resumingActivity */);
                } finally {
                    mAtmService.continueWindowLayout();
                }
                return;
            } else ......
        ......
    }

打印如下:

WindowManager: Moving to PAUSED: ActivityRecord{1f58beb u0 com.android.launcher3/.uioverrides.QuickstepLauncher} t8} (pause complete)

流程来到 TaskFragment::completePause ,注意传递过来的参数分别为 true 和 null 。

# TaskFragment
    @VisibleForTesting
    void completePause(boolean resumeNext, ActivityRecord resuming) {
        // 拿到之前的Activity,也就是需要 pause 的
        ActivityRecord prev = mPausingActivity;
        ProtoLog.v(WM_DEBUG_STATES, "Complete pause: %s", prev);
        if (prev != null) {
            ......
            // 设置 SourceActivity 对应的ActivityRecord 状态为PAUSED
            prev.setState(PAUSED, "completePausedLocked");
            if (prev.finishing) {
                ...... 如果已经finish的处理,上面还在设置 pause,那正常应该是还没finish
            } else if (prev.hasProcess()) {
                // 打印状态日志
                ProtoLog.v(WM_DEBUG_STATES, "Enqueue pending stop if needed: %s "
                        + "wasStopping=%b visibleRequested=%b",  prev,  wasStopping,prev.mVisibleRequested);
                ......
            }else {
                ......
            }
            if (resumeNext) {
                ......
                // 重点*1. 第1个分支
                mRootWindowContainer.resumeFocusedTasksTopActivities(topRootTask, prev,
                        null /* targetOptions */);
                ......
            } else {......}
            ......
            // 重点*2. 确保设置所有Activity正确的可见性,注意是3个参数的,且第一个参数为null
            mRootWindowContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS);
            ......
        }
    }

这里的 topRootTask 就是 SourceActivity “电话”的 Activity, prev 是 launcher 的 ActivityRecord , resuming 为null。
这里又分为2步走了。

3.1 第一步–resumeFocusedTasksTopActivities

执行过来的流程是上一个 Activity 完成了 pause ,那说明需要显示一个新的 Activity ,显示哪个呢?
自然是最前面的 Task 下的 最前面的 Activity ,下面看看代码中是如何实现这一步的。

直接的调用链之前看过了,所有直接看 TaskFragment::resumeTopActivity 方法,这个方法之前也看过,但是这次的逻辑肯定不一样了。

# TaskFragment

    final boolean resumeTopActivity(ActivityRecord prev, ActivityOptions options,
            boolean deferPause) {
                ......
                // 这一次没有要 pause 的Activity了,所以为false
                boolean pausing = !deferPause && taskDisplayArea.pauseBackTasks(next);
                ......
                if (pausing) {
                    ......
                    return true;
                }
                ......
                // ActivityRecord 下的 app 变量是否有值,并且进程已经执行
                if (next.attachedToProcess()) {
                    if (DEBUG_SWITCH) {
                        // 日志
                        Slog.v(TAG_SWITCH, "Resume running: " + next + " stopped=" + next.mAppStopped
                                    + " visibleRequested=" + next.isVisibleRequested());
                    }
                    ......
                } else {
                    ......
                    // 打印log
                    ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Restarting %s", next);
                    // 重点* 执行startSpecificActivity 
                    mTaskSupervisor.startSpecificActivity(next, true, true);
                    ......
                }
                return true;
            }

这一次执行 TaskFragment::resumeTopActivity 方法和之前还说有区别的,首先由于这一次没有要 pause 的 Activity 了,所以 pausing = false ,也就不会走 “if (pausing)” 内部逻辑。

走的是下面的 ActivityTaskSupervisor::startSpecificActivity 试图启动 TargetActivity ,至于最后是不是真的能启动,还是要看进程起来没。

至于"next.attachedToProcess()"这个条件,冷启动是不会走进去的,这里的逻辑我是有疑问的,放在后面 3.1.2 讲,先看主流程。

这一步会打印日志:

WindowManager: resumeTopActivity: Restarting ActivityRecord{1cf0e8 u0 com.google.android.apps.messaging/.ui.ConversationListActivity t30}

3.1.1 关键方法–startSpecificActivity

这个方法已经提过多次了,简单说目的是想去让应用端启动 Activity ,但是如果可能应用端进程还没创建,就会触发进程创建。

# ActivityTaskSupervisor

    final ActivityTaskManagerService mService;

    void startSpecificActivity(ActivityRecord r, boolean andResume, boolean checkConfig) {
        // Is this activity's application already running?
        // 拿到目标进程信息
        final WindowProcessController wpc =
                mService.getProcessController(r.processName, r.info.applicationInfo.uid);

        boolean knownToBeDead = false;
        // 重点* 1. 进程是否存在,且主线程已执行
        if (wpc != null && wpc.hasThread()) {
            try {
                // 进程存在 则执行 realStartActivityLocked 流程
                realStartActivityLocked(r, wpc, andResume, checkConfig);
                // 重点* 会返回
                return;
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception when starting activity "
                        + r.intent.getComponent().flattenToShortString(), e);1111111111
            }
            ......
        }
        ......
        // 重点* 2. 触发启动进程
        mService.startProcessAsync(r, knownToBeDead, isTop,
                isTop ? HostingRecord.HOSTING_TYPE_TOP_ACTIVITY
                        : HostingRecord.HOSTING_TYPE_ACTIVITY);
    }

以当前分析的条件,肯定又是触发进程创建了,所以 ActivityTaskSupervisor::realStartActivityLocked 暂时就先不看了。

3.1.2 疑问 – ActivityRecord::attachedToProcess

这一小节是一个小细节,倒也不影响主流程分析,如果进程启动的快,当前这个分析的流程是可以直接进入 realStartActivityLocked 方法启动 Activity 的。比如下面这个堆栈

07-10 23:22:32.960 18666 18944 E biubiubiu: 	at com.android.server.wm.ActivityTaskSupervisor.realStartActivityLocked(ActivityTaskSupervisor.java:787)
07-10 23:22:32.960 18666 18944 E biubiubiu: 	at com.android.server.wm.ActivityTaskSupervisor.startSpecificActivity(ActivityTaskSupervisor.java:1074)
07-10 23:22:32.960 18666 18944 E biubiubiu: 	at com.android.server.wm.TaskFragment.resumeTopActivity(TaskFragment.java:1551)
07-10 23:22:32.960 18666 18944 E biubiubiu: 	at com.android.server.wm.Task.resumeTopActivityInnerLocked(Task.java:5050)
07-10 23:22:32.960 18666 18944 E biubiubiu: 	at com.android.server.wm.Task.resumeTopActivityUncheckedLocked(Task.java:4980)
07-10 23:22:32.960 18666 18944 E biubiubiu: 	at com.android.server.wm.RootWindowContainer.resumeFocusedTasksTopActivities(RootWindowContainer.java:2296)
07-10 23:22:32.960 18666 18944 E biubiubiu: 	at com.android.server.wm.RootWindowContainer.resumeFocusedTasksTopActivities(RootWindowContainer.java:2282)
07-10 23:22:32.960 18666 18944 E biubiubiu: 	at com.android.server.wm.TaskFragment.completePause(TaskFragment.java:1816)
07-10 23:22:32.960 18666 18944 E biubiubiu: 	at com.android.server.wm.ActivityRecord.activityPaused(ActivityRecord.java:6399)
07-10 23:22:32.960 18666 18944 E biubiubiu: 	at com.android.server.wm.ActivityClientController.activityPaused(ActivityClientController.java:219)
07-10 23:22:32.960 18666 18944 E biubiubiu: 	at android.app.IActivityClientController$Stub.onTransact(IActivityClientController.java:663)
07-10 23:22:32.960 18666 18944 E biubiubiu: 	at com.android.server.wm.ActivityClientController.onTransact(ActivityClientController.java:153)
07-10 23:22:32.960 18666 18944 E biubiubiu: 	at android.os.Binder.execTransactInternal(Binder.java:1344)
07-10 23:22:32.960 18666 18944 E biubiubiu: 	at android.os.Binder.execTransact(Binder.java:1275)

但是在 TaskFragment::resumeTopActivity 方法有其实有"next.attachedToProcess()"这个条件判断进程是不是启动了,这个日志是直接走了 startSpecificActivity ,然后在 startSpecificActivity 方法发现进程启动了,所以就走进了 realStartActivityLocked 。
我的疑问点就是:
resumeTopActivity 到 realStartActivityLocked 中间的调用链就1层,执行时间完全可以忽略不计,难倒在这么短时间内进程刚好就启动了?

所以问题点就在于2个地方对于“进程是否启动的判断”条件。

# TaskFragment

    final boolean resumeTopActivity(ActivityRecord prev, ActivityOptions options,
            boolean deferPause) {
                ......
                // ActivityRecord 下的 app 变量是否有值,并且进程已经执行
                if (next.attachedToProcess()) {
                    ......
                } ......
            }
# ActivityTaskSupervisor

    final ActivityTaskManagerService mService;

    void startSpecificActivity(ActivityRecord r, boolean andResume, boolean checkConfig) {
        // Is this activity's application already running?
        // 拿到目标进程信息
        final WindowProcessController wpc =
                mService.getProcessController(r.processName, r.info.applicationInfo.uid);

        boolean knownToBeDead = false;
        //进程是否存在,且主线程已执行
        if (wpc != null && wpc.hasThread()) {
            
            ......
        }
        ......
    }

2个地方都是拿到 WindowProcessController 对象,判断下是不是不为空并且 hasThread 返回 true ,那么区别就在于拿到 WindowProcessController 对象的方式不一样。

后者 startSpecificActivity 方法是通过 ATMS::startSpecificActivity ,代码如下:

# ActivityTaskManagerService

    WindowProcessController getProcessController(int pid, int uid) {
        final WindowProcessController proc = mProcessMap.getProcess(pid);
        if (proc == null) return null;
        if (UserHandle.isApp(uid) && proc.mUid == uid) {
            return proc;
        }
        return null;
    }

这种方式拿到的进程信息,是靠谱的,没啥问题,再看看 ActivityRecord 的。

# ActivityRecord

    public WindowProcessController app;      // if non-null, hosting application
    void setProcess(WindowProcessController proc) {
        // 赋值
        app = proc;
        ......
    }
    boolean hasProcess() {
        return app != null;
    }

    boolean attachedToProcess() {
        return hasProcess() && app.hasThread();
    }

ActivityRecord::attachedToProcess 方法没问题,但是发现 app 这边变量是通过 ActivityRecord::setProcess 方法设置的,而这个方法的调用在 ActivityTaskSupervisor::realStartActivityLocked 里,当在这里执行 ActivityRecord::setProcess 时,应用进程肯定是真的创建了,并且 ActivityRecord 下的 app 变量也有值了。

所以冷启动没有执行到 ActivityTaskSupervisor::realStartActivityLocked 方法之前 ActivityRecord::attachedToProcess 方法 肯定是返回 false 的。

3.2 第二步–ensureActivitiesVisible 流程

前面看了 resumeFocusedTasksTopActivities 的流程,挺简单的,目前暂且假设因为进程没启动,所以没有触发 TargetActivity 的启动,现在继续看 ensureActivitiesVisible 流程。

这个方法的目的之前也说过了,就是确保一下手机上所有的 Activity 的正确可见性,也就是遍历所有 Task 下的所有 ActivityRecord 然后根据对应的条件处理想要的可见性。

# RootWindowContainer
    // starting = null  configChanges = 0  preserveWindows = false
    void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
            boolean preserveWindows) {
        ensureActivitiesVisible(starting, configChanges, preserveWindows, true /* notifyClients */);
    }

    void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
            boolean preserveWindows, boolean notifyClients) {
        // 已经正常处理可见性就不重复执行了
        if (mTaskSupervisor.inActivityVisibilityUpdate()
                || mTaskSupervisor.isRootVisibilityUpdateDeferred()) {
            // Don't do recursive work.
            return;
        }

        try {
            // 开始处理
            mTaskSupervisor.beginActivityVisibilityUpdate();
            // First the front root tasks. In case any are not fullscreen and are in front of home.
            // 遍历屏幕
            for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
                final DisplayContent display = getChildAt(displayNdx);
                display.ensureActivitiesVisible(starting, configChanges, preserveWindows,
                        notifyClients);
            }
        } finally {
            // 处理结束
            mTaskSupervisor.endActivityVisibilityUpdate();
        }
    }
  • 遍历所有屏幕
# DisplayContent

    void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
            boolean preserveWindows, boolean notifyClients) {
        ......
        mInEnsureActivitiesVisible = true;
        mAtmService.mTaskSupervisor.beginActivityVisibilityUpdate();
        try {
            forAllRootTasks(rootTask -> {
                // 遍历所有根 Task 
                rootTask.ensureActivitiesVisible(starting, configChanges, preserveWindows,
                        notifyClients);
            });
            ......
        } finally {
            mAtmService.mTaskSupervisor.endActivityVisibilityUpdate();
            mInEnsureActivitiesVisible = false;
        }
    }
  • 遍历所有根 Task ,使其执行 ensureActivitiesVisible 方法
# Task
    void ensureActivitiesVisible(@Nullable ActivityRecord starting, int configChanges,
            boolean preserveWindows, boolean notifyClients) {
        mTaskSupervisor.beginActivityVisibilityUpdate();
        try {
            // 遍历每个叶子Task
            forAllLeafTasks(task -> {
                task.updateActivityVisibilities(starting, configChanges, preserveWindows,
                        notifyClients);
            }, true /* traverseTopToBottom */);
            ......
        } finally {
            mTaskSupervisor.endActivityVisibilityUpdate();
        }
    }
  • 遍历所有叶子 Task ,使其执行 updateActivityVisibilities 方法,这个方法定义在父类 TaskFragmet 中。
# TaskFragmet
    // 帮助类
    private final EnsureActivitiesVisibleHelper mEnsureActivitiesVisibleHelper =
            new EnsureActivitiesVisibleHelper(this);

    final void updateActivityVisibilities(@Nullable ActivityRecord starting, int configChanges,
            boolean preserveWindows, boolean notifyClients) {
        mTaskSupervisor.beginActivityVisibilityUpdate();
        try {
            // 重点处理
            mEnsureActivitiesVisibleHelper.process(
                    starting, configChanges, preserveWindows, notifyClients);
        } finally {
            mTaskSupervisor.endActivityVisibilityUpdate();
        }
    }

我们知道这一条线都是为了处理Activity可见的。 在这定义了一个专门的类来处理。
不过需要注意的是,这个方法会执行多次,因为他是遍历每一个符合条件的子容器,从上到下遍历。

3.2.1 EnsureActivitiesVisibleHelper::process

这个方法是调用 EnsureActivitiesVisibleHelper 的的方法,Task 通过这个方法来处理当前容器下的元素可见性。

# EnsureActivitiesVisibleHelper

    void process(@Nullable ActivityRecord starting, int configChanges, boolean preserveWindows, boolean notifyClients) {
        // 1. 对参数进行重置处理
        reset(starting, configChanges, preserveWindows, notifyClients);
        ......

        final boolean resumeTopActivity = mTopRunningActivity != null
                && !mTopRunningActivity.mLaunchTaskBehind
                && mTaskFragment.canBeResumed(starting)
                && (starting == null || !starting.isDescendantOf(mTaskFragment));
        // 遍历容器下所有元素
        for (int i = mTaskFragment.mChildren.size() - 1; i >= 0; --i) {
            // 获取当前子元素
            final WindowContainer child = mTaskFragment.mChildren.get(i);
            // 将当前子元素转换为TaskFragment(只有TaskFragment重写了asTaskFragment()方法),Task或ActivityRecord为null
            // 而Task的孩子一般就是ActivityRecord或者Task。 当前分析逻辑(大部分场景)Task下面是ActivityRecord
            // 所以这里childTaskFragment 为 null
            final TaskFragment childTaskFragment = child.asTaskFragment();
            if (childTaskFragment != null
                    && childTaskFragment.getTopNonFinishingActivity() != null) {
                ......
            } else {
                // 只有ActivityRecord重写了 asActivityRecord()
                setActivityVisibilityState(child.asActivityRecord(), starting, resumeTopActivity);
            }
        }
    }

执行到这里对当前的 Task 遍历了他下面的所有元素,我们目前关系的是 Activity 所以看对每个 ActivityRecord 做了什么 。

3.2.2 EnsureActivitiesVisibleHelper::setActivityVisibilityState

# EnsureActivitiesVisibleHelper

    private void setActivityVisibilityState(ActivityRecord r, ActivityRecord starting,
        final boolean resumeTopActivity) {
        ......
        if (reallyVisible) {
            .......
            if (!r.attachedToProcess()) {
                // 主流程
                makeVisibleAndRestartIfNeeded(mStarting, mConfigChanges, isTop,
                        resumeTopActivity && isTop, r);
            } else {
                ......
            }
        } else {
            ......
            // 不可见调用makeInvisible
            r.makeInvisible();
        }
    }
  • 这里的 r 表示当前Task 下的 ActivityRecord 。
  • 所有 ActivityRecord 都会执行这个方法,目前关心的是 SourceActivity 和 TargetActivity 。
  • SourceActivity 也就是 Launcher 走到这里会执行 “r.makeInvisible();” 进而触发 onStop 逻辑
  • TargetActivity 也就是 “电话” 走到这里则是去走可见逻辑

SourceActivity 的就不管了,不是主线,继续看 TargetActivity 在 makeVisibleAndRestartIfNeeded 方法是怎么处理的。

# EnsureActivitiesVisibleHelper
    private void makeVisibleAndRestartIfNeeded(ActivityRecord starting, int configChanges,
            boolean isTop, boolean andResume, ActivityRecord r) {
        ......
        if (!r.mVisibleRequested || r.mLaunchTaskBehind) {
            if (DEBUG_VISIBILITY) {
                Slog.v(TAG_VISIBILITY, "Starting and making visible: " + r);
            }
            // 1. 设置Visibility
            r.setVisibility(true);
        }
        if (r != starting) {
            // 2. 试图启动 Activity
            mTaskFragment.mTaskSupervisor.startSpecificActivity(r, andResume,
                    true /* checkConfig */);
        }
    }
    1. 设置可见。 这个是 ensureActivitiesVisible 流程最主要做的事,这个流程就是为了确保所有 Activity 正确的可见性 。
    1. 试图启动 Activity 。 发现当前的显示的 Activity 和正在处理的 Activity 不是一个,则视图启动。(starting 这个参数之前注释了为null)

后面的 ActivityTaskSupervisor::startSpecificActivity 方法就不看了,刚看过了,如果进程启动了才会执行后续逻辑启动 TargetActivity。

4. 阶段二总结

阶段二的逻辑其实比较简单,只是我分析的比较细节。
阶段二其实就是 SourceActivity 完成 pause 后,执行 ActivityRecord::activityPaused 流程,AMS 需要显示当前顶层 Activity 所以执行了 RootWindowContainer::resumeFocusedTasksTopActivities 方法,但是这一次是不是真的能显示顶层 Activity 还是要看其进程是否已经创建好了。

在操作了 Activity 的显示逻辑后,为了确保系统 Activity 正常的可见性,所以又执行了一次 ensureActivitiesVisible 流程。

流程相对简单,就是下面这个图:
在这里插入图片描述


http://www.kler.cn/a/325681.html

相关文章:

  • 视频融合×室内定位×数字孪生
  • .NET6 WebApi第1讲:VSCode开发.NET项目、区别.NET5框架【两个框架启动流程详解】
  • 10万字208道Java经典面试题总结(2024修订版)- SpringBoot篇下篇
  • 【Swift】类型标注、类型安全和类型推断
  • MySQL安装及数据库基础
  • 天童美语:下元节将至
  • 大模型使用vLLM推理加速
  • 一篇讲完HTML核心内容
  • 超大规模钢筋计数数据集,共23400组图像,多视角,多角度,多场景,采用voc方式标注 智慧工地资产盘点
  • 【Linux 22】生产者消费者模型
  • 多线程(一):线程的基本特点线程安全问题ThreadRunnable
  • 新买的笔记本电脑如何打开和使用显示卡的问题
  • Windows11系统下Docker环境搭建教程
  • 每天一个数据分析题(四百七十四)- 柱状图
  • golang reflect
  • unity 如何 团队协作避免文件冲突?
  • 企业微信群发系统:精准触达,高效管理的营销新引擎
  • 初识Linux · 地址空间
  • 最新版ChatGPT对话系统源码 Chat Nio系统源码
  • Redis实战--Redis的数据持久化与搭建Redis主从复制模式和搭建Redis的哨兵模式
  • Win10系统使用mstsc远程电脑的时候发现隔一段时间就无法使用剪贴板_rdpclip---Windows运维工作笔记055
  • 第五节-C++类和对象(五):友元机制、内部类与匿名对象
  • Spring MVC 参数校验 总结
  • PyGWalker:让你的Pandas数据可视化更简单,快速创建数据可视化网站
  • 【Linux】常用命令
  • 【easypoi 一对多导入解决方案】