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

news2024/11/18 18:18:39

忽然有一天,我想要做一件事:去代码中去验证那些曾经被“灌输”的理论。
                  
                  
                                           – 服装学院的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.coloradmin.cn/o/2177766.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

高并发内存池(六):补充内容

目录 有关大于256KB内存的申请和释放处理方法 处理大于256KB的内存申请 补充内容1 补充内容2 补充内容3 处理大于256KB的内存释放 新增内容1 新增内容2 测试函数 使用定长内存池替代new 释放对象时不传对象大小 补充内容1 补充内容2 补充内容3 补充内容4 测试…

Python(五)-函数

目录 函数的定义与调用 特点 语法格式 函数的参数 函数的返回值 函数嵌套调用 变量的作用域 局部变量 全局变量 函数的多种参数 位置参数 关键字参数 默认参数 可变参数 函数的定义与调用 python函数需要使用def关键字来定义,需要先定义,后调用 特点: 先定义…

课堂讨论:评价计算机性能的指标

**课堂讨论:评价计算机性能的指标** --- ### 课堂开始 **王老师**:同学们,今天我们来讨论如何评价计算机性能的指标。小明,你知道有哪些指标吗? **小明**:嗯...有吞吐率和响应时间吧?&#…

双链表的插入删除遍历

双链表的插入操作 双链表的删除操作 双链表的遍历操作

Watchdog Timers(WDT)

文章目录 1. 介绍2. Feature List3. 概述3.1. Safety Watchdog3.2. CPU Watchdog 4. 看门狗定时器功能5. Endinit Functions5.1 Password Access to WDTxCON05.1.1 Static Password5.1.2 Automatic Password Sequencing 5.2 Check Access to WDTxCON05.3 Modify Access to WDTx…

点餐小程序实战教程13餐桌管理

目录 1 创建数据源2 搭建管理后台3 生成餐桌码4 找到自己的appid和secret5 小程序里获取餐桌信息总结 我们上一篇介绍了点餐界面的菜品展示功能。现实中如果你去餐馆用餐,总是给餐桌贴一个二维码,服务员会告诉你扫码点餐。 扫码大家现在都已经非常熟练了…

“从零开始学排序:简单易懂的算法指南“

“一辈人有一辈人要做的事!!!” 这一期的节目呢,是关于排序的内容,相信大家对此一定很熟悉吧! 排序: 排序是将一组元素按照一定的规则或标准进行组织和排列的过程。 冒泡排序: 冒…

此连接非私人连接

当你手机浏览器输入网站打开提示“此连接非私人连接,此网站可能在冒充来窃取你的个人或财务信息。你应回到之前的页面”这是因为该网站的SSL数字证书到期导致,需要此网站的管理员重新申请数字证书替换之前的文件才可以实现。 注意:如果你不是…

Token: 数据库、存储系统和API安全的应用

一. Token Token是一种常见的计算机术语,它在不同的上下文中有不同的含义。在身份验证和授权的上下文中,Token通常指的是服务端生成的一串字符串,作为客户端进行请求的一个令牌。当用户登录后,服务器会生成一个Token并返回给客户…

【高阶数据结构】平衡二叉树(AVL)的删除和调整

🤡博客主页:醉竺 🥰本文专栏:《高阶数据结构》 😻欢迎关注:感谢大家的点赞评论关注,祝您学有所成! ✨✨💜💛想要学习更多《高阶数据结构》点击专栏链接查看&a…

记一次教学版内网渗透流程

信息收集 如果觉得文章写的不错可以共同交流 http://aertyxqdp1.target.yijinglab.com/dirsearch dirsearch -u "http://aertyxqdp1.target.yijinglab.com/"发现 http://aertyxqdp1.target.yijinglab.com/joomla/http://aertyxqdp1.target.yijinglab.com/phpMyA…

DialFRED基准:具有对话能力的具身智能Agent

目录 一、DialFRED数据集1.1 数据集规模与任务结构1.2 任务实例的构成1.3 人类标注的问答数据1.4 Oracle自动生成答案1.5 任务多样性与数据增强1.6 数据集的词汇多样性1.7 任务和环境的多样性 二、提问者-执行者框架2.1 框架概述2.2 提问者模型设计2.3 执行者模型设计2.4 强化学…

【读书笔记-《30天自制操作系统》-25】Day26

本篇仍然是围绕着命令行窗口做文章。首先优化命令行窗口的移动速度,然后增加多个命令行窗口功能。接着优化了命令行窗口的关闭,最后增加了两个命令start与ncst。 1. 优化命令行窗口移动速度 首先对命令行窗口的移动速度进行优化。主要的优化点有以下几…

WEB服务器——Tomcat

服务器是可以使用java完成编写,是可以接受页面发送的请求和响应数据给前端浏览器的,而在开发中真正用到的Web服务器,我们不会自己写的,都是使用目前比较流行的web服务器。 如:Tomcat 1. 简介 Tomcat 是一个开源的轻量…

二维数组的存放

今天我水的文章是二维数组的存放 二维数组的存放方式其实和一维数组没有区别&#xff0c;但如果想要更直观的了解&#xff0c;我们可以把它们的地址打印出来。 代码如下&#xff1a; #include <stdio.h> int main() {int arr[3][3];//二维数组&#xff0c;int数组类型…

【高效管理集合】并查集的实现与应用

文章目录 并查集的概念主要操作优化技术应用场景 并查集的实现基本框架并查集的主要接口总体代码 并查集的应用省份的数量等式方程的可满足性 总结 并查集的概念 并查集&#xff0c;也称为不相交集&#xff0c;是一种树形的数据结构&#xff0c;用于处理一些不相交集合的合并及…

ClickHouse | 查询

1 ALL 子句 2 ARRAY JOIN 使用别名 :在使用时可以为数组指定别名&#xff0c;数组元素可以通过此别名访问&#xff0c;但数组本身则通过原始名称访问 3 DISTINCT子句 DISTINCT不支持当包含有数组的列 4 FROM子句 FROM 子句指定从以下数据源中读取数据: 1.表 2.子…

建筑资质应该怎么选?

建筑资质是建筑企业承接工程项目的必备条件&#xff0c;它不仅关系到企业的市场竞争力&#xff0c;还直接影响到企业的经营效益。因此&#xff0c;选择适合自己企业的建筑资质至关重要。以下是一些选择建筑资质时需要考虑的关键因素&#xff1a; 1. 明确企业定位 首先&#x…

金融教育宣传月 | 平安养老险百色中心支公司开展金融知识“消保县域行”宣传活动

9月22日&#xff0c;平安养老险百色中心支公司积极落实国家金融监督管理总局关于开展金融教育宣传月活动的相关要求&#xff0c;联合平安人寿百色中心支公司共同组成了平安志愿者小队&#xff0c;走进百色市四塘镇百兰村开展了一场别开生面的金融消费者权益保护宣传活动。此次活…

如何给你的项目添加测试覆盖率徽章

看完我的测试教程之后&#xff0c;想必大家都能写出一个测试覆盖率极高的小项目了。测试覆盖率既然这么高&#xff0c;不秀一秀岂不是白瞎了&#xff0c;下面我们就来通过第三方服务来给你的项目加上测试覆盖率徽章&#xff0c;涉及到的内容有yaml配置&#xff0c;githubAction…