【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 启动“电话”的场景来看看执行逻辑。

# TaskFragmentfinal boolean resumeTopActivity(ActivityRecord prev, ActivityOptions options,boolean deferPause) {// 这里的next返回的是电话的 MainActivity,表示下一个需要显示的ActivityActivityRecord next = topRunningActivity(true /* focusableOnly */);......// 如果跳过的这些逻辑都没执行return,则正在开始执行 resume 流程,打印关键日志。    // 前面流程如果返回也有日志的打印// 打印日志,需要显示哪个Activityif (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);......// 重点* 1. 这里会将 Launcher 的Activity pause 。参数是电话的 ActivityRecord// 注意 1:如果有 Activity 被 pauseBackTasks 方法触发了 pause,则返回 true,表示有 Activity 正在 pausingboolean 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:这里会returnreturn 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在这里就被称为resumingboolean 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 。

# TaskFragmentboolean 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状态为PAUSINGprev.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);// 重点* 构建并执行PauseActivityItemmAtmService.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@Overridepublic 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);}@Overridepublic int getTargetState() {return ON_PAUSE;}//  pauser执行后调用 postExecute@Overridepublic void postExecute(ClientTransactionHandler client, IBinder token,PendingTransactionActions pendingActions) {if (mDontReport) {return;}//  重点*2. 触发启动新的ActivityActivityClient.getInstance().activityPaused(token);}

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

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

# ActivityClientpublic 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::startActivityActivityTaskManagerService::startActivityAsUserActivityTaskManagerService::startActivityAsUserActivityStartController::obtainStarterActivityStarter::executeActivityStarter$Request::resolveActivity           -- 解析启动请求参数ActivityTaskSupervisor::resolveIntentActivityTaskSupervisor::resolveActivityActivityStarter::executeRequest                    -- 3.3 创建ActivityRecordActivityStarter::startActivityUncheckedActivityStarter::startActivityInner        -- 3.4 关键函数startActivityInnerActivityStarter::getOrCreateRootTask   -- 3.4.1 创建或者拿到TaskRootWindowContainer::getOrCreateRootTaskRootWindowContainer::getOrCreateRootTaskTaskDisplayArea::getOrCreateRootTaskTaskDisplayArea::getOrCreateRootTaskTask::Build     ---创建Task                    ActivityStarter::setNewTask            -- 3.4.2 将Task与activityRecord 绑定ActivityStarer::addOrReparentStartingActivityTask::moveToFront                       --3.4.3 移动Task到栈顶Task::moveToFrontInnerTaskDisplayArea::positionChildAtTaskDisplayArea::positionChildTaskAtActivityTaskSupervisor::updateTopResumedActivityIfNeededActivityRecord::onTopResumedActivityChanged      --触发TopResumedActivityChangeItemRootWindowContainer::resumeFocusedTasksTopActivities    --3.4.4 显示ActivityTask::resumeTopActivityUncheckedLockedTask::resumeTopActivityInnerLockedTaskFragment::resumeTopActivity      -- 1. 显示顶层ActivityTaskDisplayArea::pauseBackTasks  -- 1.2 pause LauncherActivity WindowContainer::forAllLeafTaskTaskFragment::forAllLeafTaskFragmentsTaskFragment::startPausingTaskFragment::startPausingTaskFragment::schedulePauseActivity --构建 PauseActivityItem,这里是触发暂停launchActivityTaskManagerService::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::activityPausedActivityRecord::activityPausedTaskFragment::completePauseRootWindowContainer::resumeFocusedTasksTopActivities       -- 显示顶层ActivityRootWindowContainer::resumeFocusedTasksTopActivitiesTask::resumeTopActivityUncheckedLockedTask::resumeTopActivityInnerLockedTaskFragment::resumeTopActivityActivityTaskSupervisor::startSpecificActivity        -- 试图启动 ActivityRootWindowContainer::ensureActivitiesVisible              -- 确保设备上 Activity 的可见性RootWindowContainer::ensureActivitiesVisibleDisplayContent::ensureActivitiesVisible WindowContainer::forAllRootTasks  --忽略固定逻辑Task::ensureActivitiesVisibleTask::forAllLeafTasks --忽略固定逻辑TaskFragment::updateActivityVisibilitiesEnsureActivitiesVisibleHelper::processEnsureActivitiesVisibleHelper::setActivityVisibilityStateEnsureActivitiesVisibleHelper::makeVisibleAndRestartIfNeededActivityTaskSupervisor::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 方法开始。

# ActivityRecordvoid 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@VisibleForTestingvoid completePause(boolean resumeNext, ActivityRecord resuming) {// 拿到之前的Activity,也就是需要 pause 的ActivityRecord prev = mPausingActivity;ProtoLog.v(WM_DEBUG_STATES, "Complete pause: %s", prev);if (prev != null) {......// 设置 SourceActivity 对应的ActivityRecord 状态为PAUSEDprev.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个参数的,且第一个参数为nullmRootWindowContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS);......}}

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

3.1 第一步–resumeFocusedTasksTopActivities

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

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

# TaskFragmentfinal boolean resumeTopActivity(ActivityRecord prev, ActivityOptions options,boolean deferPause) {......// 这一次没有要 pause 的Activity了,所以为falseboolean 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 {......// 打印logProtoLog.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 ,但是如果可能应用端进程还没创建,就会触发进程创建。

# ActivityTaskSupervisorfinal 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个地方对于“进程是否启动的判断”条件。

# TaskFragmentfinal boolean resumeTopActivity(ActivityRecord prev, ActivityOptions options,boolean deferPause) {......// ActivityRecord 下的 app 变量是否有值,并且进程已经执行if (next.attachedToProcess()) {......} ......}
# ActivityTaskSupervisorfinal 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 ,代码如下:

# ActivityTaskManagerServiceWindowProcessController 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 的。

# ActivityRecordpublic WindowProcessController app;      // if non-null, hosting applicationvoid 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 = falsevoid 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();}}
  • 遍历所有屏幕
# DisplayContentvoid 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 方法
# Taskvoid ensureActivitiesVisible(@Nullable ActivityRecord starting, int configChanges,boolean preserveWindows, boolean notifyClients) {mTaskSupervisor.beginActivityVisibilityUpdate();try {// 遍历每个叶子TaskforAllLeafTasks(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 通过这个方法来处理当前容器下的元素可见性。

# EnsureActivitiesVisibleHelpervoid 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 为 nullfinal 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

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

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

# EnsureActivitiesVisibleHelperprivate 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. 设置Visibilityr.setVisibility(true);}if (r != starting) {// 2. 试图启动 ActivitymTaskFragment.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.rhkb.cn/news/435217.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PingCastle:一款针对活动目录AD的安全强化工具

关于PingCastle PingCastle是一款针对活动目录AD的安全强化工具,可以帮助广大研究人员提升活动目录的安全性,该工具甚至可以做到在 20% 的时间内实现 80% 的AD安全性。 Ping Castle 是一种旨在使用基于风险评估和成熟度框架的方法快速评估活动目录AD 安…

pywinauto,一款Win自动化利器!

1.安装 pywinauto是一个用于自动化Python模块,适合Windows系统的软件(GUI),可以通过Pywinauto遍历窗口(对话框)和窗口里的控件,也可以控制鼠标和键盘输入,所以它能做的事情比之前介…

大数据实时数仓Hologres(一):Hologres 简单介绍

文章目录 Hologres 简单介绍 一、什么是实时数仓 Hologres 二、产品优势 1、专注实时场景 2、亚秒级交互式分析 3、统一数据服务出口 4、开放生态 5、MaxCompute查询加速 6、计算存储分离架构 三、应用场景 搭建实时数仓 四、产品架构 1、Shared Disk/Storage &am…

Appium独立测试自动化初始化脚本

1、查看环境初始化参数 确保appium已经开起来了,设置ip ,并点击启动 打开夜神模拟器,点击工具--设置 最下面的版本说明,双击进去 版本号这里再去单击。 直到进入到开发者模式。 可能我们不是开发者模式打开的状态,所以软件访问模…

我摊牌了!!程序员不卷了,AI大模型先转一步!

计算机行业小伙伴儿们的归宿到底在哪里啊? 互联网和程序员的风口已经过去了,作为一名大龄程序员,在继续卷和转型发展之间我选择了转行AI大模型。 4年更火的无非就是人工智能了,大家在招聘网站也可以看到名种各样的offer&#x…

基于Java+VUE+echarts大数据智能道路交通信息统计分析管理系统的设计与实现

大数据智能交通管理系统是一种基于Web的系统架构,通过浏览器/服务器(B/S)模式实现对城市交通数据的高效管理和智能化处理。该系统旨在通过集成各类交通数据,包括但不限于车辆信息、行驶记录、违章情况等,来提升城市管理…

天龙八部怀旧单机微改人面桃花+安装教程+GM工具+虚拟机一键端

今天给大家带来一款单机游戏的架设:天龙八部怀旧单机微改人面桃花。 另外:本人承接各种游戏架设(单机联网) 本人为了学习和研究软件内含的设计思想和原理,带了架设教程仅供娱乐。 教程是本人亲自搭建成功的&#xf…

画个心,写个花!Python Turtle库带你玩转创意绘图!

文章目录 前言一、Turtle 库基础介绍二、画布设置三、画笔属性设置1.画笔颜色设置2.画笔粗细与速度设置3.画笔形状设置 四、画笔移动函数五、画笔控制函数六、实战案例一:“花”字绘制七、实战案例二:心型图案绘制总结 前言 Python 的 turtle 库是一种简…

关于宝塔PHP getenv无法获取环境变量问题解决办法

今天有用ThinkPHP8接入阿里云OSS时,需要用的用到getenv()来读取环境变量,因为新版OSS SDK是用环境变更来设置AK的。 现象 正常执行PHP文件,可以取到环境变量;但是通过nginxphp-fpm调用脚本取到不到环境变量 原因 php-fpm为了防止…

[spring]用MyBatis XML操作数据库 其他查询操作 数据库连接池 mysql企业开发规范 动态sql

文章目录 一. MyBatis XML配置文件1. 配置链接字符串和MyBatis2. 写持久层代码方法定义Interface方法实现xml测试 3. 增删改查增:删改查 二. 开发规范(mysql)三. 其他查询操作1. 多表查询2. #{} 和 ${}(面试题)使用区别 排序功能like查询 三. 数据库连接池四. 动态sql1. < i…

使用shc|gzexe加密shell脚本,并配置全局可用

前言&#xff1a;堡垒机上多厂家共用&#xff0c;导致脚本需要加密并设置过期时间 一、使用shc&#xff08;二进制&#xff09; 1、安装shc sudo yum install epel-release sudo yum -y install shc sudo yum -y instal epel-release && sudo yum -y install shc 2、…

图像增强论文精读笔记-Deep Retinex Decomposition for Low-Light Enhancement(Retinex-Net)

1. 论文基本信息 论文标题&#xff1a;Deep Retinex Decomposition for Low-Light Enhancement 作者&#xff1a;Chen Wei等 发表时间和期刊&#xff1a;2018&#xff1b;BMVC 论文链接&#xff1a;https://arxiv.org/abs/1808.04560 2. 研究背景和动机 低光照条件下拍摄的…

计算机毕业设计 基于 Hadoop平台的岗位推荐系统 SpringBoot+Vue 前后端分离 附源码 讲解 文档

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

tauri开发软件中,使用tauri自带的api用浏览器打开指定的url链接

有能力的可以看官方文档&#xff1a;shell | Tauri Apps 就是使用这个api来打开指定的url链接&#xff0c;要在tauri.config.json中配置打开这个api&#xff1a; 然后在前端页面中导入使用&#xff1a; import { open } from tauri-apps/api/shell; // opens the given URL o…

ODA(Open Design Alliance)试用小记-ODA提供源码下载就完全可控了吗?

1.概述 ODA(Open Design Alliance)库架构如下&#xff1a; 产品体系如下&#xff1a; ODA的产品体系越来越壮大&#xff0c;包括主流BIM格式SDK、Viewer、Cloud、数据交换等&#xff0c;每个模块需要单独购买&#xff0c;并提供“源码服务”。 2.是否可控&#xff1f; 值得…

【rust】 基于rust编写wasm,实现markdown转换为html文本

文章目录 背景转换预览核心代码前置依赖rustup换源 cargo本地路径修改&#xff08;可选&#xff09;cargo换源中科大 wasm-pack安装 背景 尝试用rust编写一款markdown转html的插件&#xff0c;通过wasm给html使用&#xff0c;不得不说体积挺小&#xff0c;约200K&#xff0c; …

深度学习——D2(数据操作)

N维数组 创建数组 访问元素 一列: [ : , 1 ] 反向累积、正向累积&#xff08;自动求导&#xff09; 梯度 梯度&#xff08;Gradient&#xff09;是微积分中的一个重要概念&#xff0c;主要用于描述一个函数在某个区域内的变化情况。以下是对梯度的详细解释&#xff1a; 一…

Qorvo® 推出具有卓越能效的新一代 Matter™ 解决方案

全新 SoC 利用 ConcurrentConnect™ 技术实现智能家居的无缝互联 Qorvo今日宣布&#xff0c;推出面向智能家居设备的全新片上系统&#xff08;SoC&#xff09;解决方案——QPG6200L&#xff0c;并已向主要客户提供样品。该款下一代物联网&#xff08;IoT&#xff09;解决方案采…

数据结构双链表和循环链表

目录 一、循环链表二、双向链表三、循环双向链表 一、循环链表 循环链表就是首尾相接的的链表&#xff0c;就是尾节点的指针域指向头节点使整个链表形成一个循环&#xff0c;这就弥补了以前单链表无法在后面某个节点找到前面的节点&#xff0c;可以从任意一个节点找到目标节点…

【YashanDB知识库】如何配置jdbc驱动使getDatabaseProductName()返回Oracle

本文转自YashanDB官网&#xff0c;具体内容请见https://www.yashandb.com/newsinfo/7352676.html?templateId1718516 问题现象 某些三方件&#xff0c;例如 工作流引擎activiti&#xff0c;暂未适配yashandb&#xff0c;使用中会出现如下异常&#xff1a; 问题的风险及影响 …