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

【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 流程。

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

相关文章:

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

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

ubuntu 18.04 cuda 11.01 gpgpu-sim 裸机编译

1,环境 ubuntu 18.04 x86_64 cuda 11.01 gpgpu-sim master commit 90ec3399763d7c8512cfe7dc193473086c38ca38 2,预备环境 一个比较新的 ubuntu 18.04,为了迎合 cuda 11.01 的版本需求 安装如下软件: sudo apt-get instal…...

【Kubernetes】常见面试题汇总(五十二)

目录 116. K8S 集群服务暴露失败? 117.外网无法访问 K8S 集群提供的服务? 特别说明: 题目 1-68 属于【Kubernetes】的常规概念题,即 “ 汇总(一)~(二十二)” 。 题目 69-…...

o1-preview 在 IMO 2024 第一题的实测表现

相关博客:Learning to Reason with LLMs 以及 Introducing OpenAI o1-preview 测试了 IMO 2024 的第一题,OpenAI-o1-preview 的解题过程包括两部分:思考和推理。 正确答案是全体偶数,o1-preview 的 **思考方向正确,推…...

iOS--RunLoop原理

前言 曾经在写项目的时候遇到过这么一个问题。: 项目中添加了一个tableview,然后还有一个计时器,当滑动tableview的时候会阻塞计时器,你得执行这么一段代码后,计时器才能正常运行。 RunLoop.current.add(timer, for…...

并查集——从LeetCode题海中总结常见套路

目录 并查集定义 LeetCode128.最长连续序列 先去重再sort: 改进去重的方法: 参考: 并查集定义 在计算机科学中,并查集是一种树型的数据结构,用于处理一些不交集(Disjoint Sets)的合并及查…...

深入理解作用域【JavaScript】

一、作用域的内部原理 JavaScript 的作用域机制是理解变量如何被访问和存储的重要概念。下面详细介绍作用域的内部原理,包括编译、执行、查询、嵌套和异常处理这五个步骤。 1. 编译 在 JavaScript 的执行过程中,首要的步骤是编译。尽管JavaScript是解…...

微信小程序实战教程:如何使用map组件实现地图功能

在微信小程序中,map组件是一个非常实用的功能,它可以帮助我们快速实现地图展示、定位、标注等操作。本文将详细介绍如何在微信小程序中使用map组件,带你轻松掌握地图开发技能。 一、map组件概述 map组件是微信小程序官方提供的一个地图组件…...

张雪峰谈人工智能技术应用专业的就业前景!

一、张雪峰谈人工智能技术应用专业 在教育咨询领域,张雪峰老师以其深入浅出的讲解和前瞻性的视角,为广大学子提供了宝贵的专业选择建议。对于人工智能技术应用专业,张雪峰老师通常给予高度评价,认为这是一个充满无限可能且就业前…...

机器学习课程学习周报十五

机器学习课程学习周报十五 文章目录 机器学习课程学习周报十五摘要Abstract一、机器学习部分1. 统计推断与贝叶斯推断2. GMM和EM算法补充3. 马尔可夫链蒙特卡罗法3.1 蒙特卡罗法3.2 马尔可夫链3.3 Diffusion模型中的马尔可夫链 总结 摘要 本周的学习涵盖了统计推断和贝叶斯推断…...

rabbitMq------客户端模块

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言消费者模块信道管理模块管理的字段提供的接口 信道内存管理连接管理类 前言 在RabbitMQ中,提供服务的是信道,因此在客⼾端的实现中&…...

地理定位营销与开源AI智能名片O2O商城小程序的融合与发展

摘要:本文阐述地理定位营销的概念、手段及其在商业中的应用,探讨开源AI智能名片O2O商城小程序如何与地理定位营销相结合,为企业营销带来新的机遇与挑战。 一、引言 在当今数字化营销的时代,地理定位营销已成为一种重要的营销手段…...

解决Vue应用中遇到路由刷新后出现 404 错误

解释: Vue 应用中遇到路由刷新后出现 404 错误,通常是因为 Vue 应用是个单页应用(SPA),它通过 Vue Router 管理路由,通过 HTML5 History Mode 实现页面导航无需重新加载页面。当直接访问非首页的路由或者刷…...

在window10下使用directml加速phi-3模型的一些记录

1.安装anaconda,安装python 安装torch等参考网上资料非常多 不细描述 2.参考微软官网【在windows上通过DirectML启用Pytorch文档,检查系统版本 检查gpu版本 3.参考微软官网【在windows上通过DirectML启用Pytorch】文档,安装torch_directml模…...

通信工程学习:什么是OSPF开放式最短路径优先

OSPF:开放式最短路径优先 OSPF(Open Shortest Path First,开放式最短路径优先)是一种内部网关协议(IGP),被广泛应用于计算机网络中,特别是在构建大型和复杂的网络时。以下是对OSPF的…...

《中国电子报》报道: 安宝特AR为产线作业者的“秘密武器

近日,中国电子报在其文章《下一代工业智能终端重新定义制造业》中对安宝特的增强现实(AR)解决方案给予了高度评价,称其为产线作业者的“秘密武器”。这一创新技术改变了传统制造业的作业方式,使得操作人员能够在生产过…...

【Android】Handler消息机制

文章目录 前言概述核心组件概述Android消息机制概述 Android消息机制分析ThreadLocal的工作原理ThreadLocal基础ThreadLocal实现原理 MessageQueueLooperHandler的工作原理总结 前言 本文用于记录Android的消息机制,主要是指Handler的运行机制。部分内容参考自《An…...

大数据必懂知识点:Parquet、ORC还是Avro作为数据存储格式,哪种在性能和压缩率上更优

目录 第一章 相关理论 1.1 Parquet格式介绍 1.1.1 起源与发展 1.1.2 特点与优势 1.2 ORC格式介绍 1.3 Avro格式介绍 1.3.1 跨语言支持 1.3.2 动态映射 1.3.3 丰富的数据模式 1.3.4 数据模式灵活性 第二章 种格式性能比较 2.1 读写性能对比 2.2 查询性能对比 2.3 压…...

P1387 最大正方形

题目描述 在一个nm 的只包含 0 和 1 的矩阵里找出一个不包含 0 的最大正方形,输出边长。 输入格式 输入文件第一行为两个整数n,m(1≤n,m≤100),接下来 n 行,每行 m 个数字,用空格隔开,0 或 1。 输出格式 一个整数…...

Python知识点:如何使用Multiprocessing进行并行任务管理

开篇,先说一个好消息,截止到2025年1月1日前,翻到文末找到我,赠送定制版的开题报告和任务书,先到先得!过期不候! 如何在Python中使用Multiprocessing进行并行任务管理 在现代编程中,…...

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

Ubuntu系统下交叉编译openssl

一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机:Ubuntu 20.04.6 LTSHost:ARM32位交叉编译器:arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...

React Native 导航系统实战(React Navigation)

导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...

Objective-C常用命名规范总结

【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...

最新SpringBoot+SpringCloud+Nacos微服务框架分享

文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...

如何为服务器生成TLS证书

TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

.Net Framework 4/C# 关键字(非常用,持续更新...)

一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

QT3D学习笔记——圆台、圆锥

类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体(对象或容器)QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质(定义颜色、反光等)QFirstPersonC…...

【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案

目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后,迭代器会失效,因为顺序迭代器在内存中是连续存储的,元素删除后,后续元素会前移。 但一些场景中,我们又需要在执行删除操作…...

淘宝扭蛋机小程序系统开发:打造互动性强的购物平台

淘宝扭蛋机小程序系统的开发,旨在打造一个互动性强的购物平台,让用户在购物的同时,能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机,实现旋转、抽拉等动作,增…...