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

server/2024/12/23 0:20:35/

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

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

正文

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

Activity启动流程-1

Activity启动流程-2

Activity启动流程-3

Activity 的启动流程无论是 APP 开发还是 FrameWork 开发都是必须要熟悉的一个流程。

FrameWork 中有很多重要的流程都是在 Activity 启动过程中触发的,不过当前还是以分析 Activity 启动主流程为主,当然对于一些关键方法还是会着重介绍,这样以后如果有遇到相关问题的修改可以通过这篇文章找到具体代码位置,然后根据具体的问题分析修改。

启动 Activity 的方式有很多,当前以在 Launcher 点击“电话”图标启动应用场景进行分析。

1. 整体介绍

1.1 一级框图

在这里插入图片描述
对于Activity的启动流程来说,可以分为3个模块:

SourceActivity:执行 startActivity 方法的 Activity ,也就是发起请求的Activity ,当前是 Launcher 的 Activity 。

TargetActivity:被启动的 Activity 当前就是“电话”应用在清单文件配置的MainActivity

AMS: 不仅仅是指 AMS 这一个类,而是指在这个过程中 system_service 进程参与处理的相关类

举个例子,以在公司的工作流程来说, Launcher 模块的开发,在处理一个 bug,但是涉及到了通话,那么他需要找到通讯组的同事来处理这个问题。但公司很大,他并不知道通讯模块是谁负责,更不知道这个问题需要交给通讯组具体的哪个同事处理,那么他只需要将自己的要求向公司领导(管理着)汇报:需要通讯组的同事处理这个问题。

当前例子设计到 launcher 和通讯2个模块的开发人员,还涉及到公司的管理者。在Activity 启动也是如此, 对于 SourceActivity、TargetActivity 来说他们并不知道对方模块的业务,所以这一流程需要AMS来做管理。

并且,这3个模块也对应3个进程,当前案例来说分别为:Launcher 进程,电话进程,system_service 进程。

图中AMS 对 Launcher 多了一个返回箭头的原因是 Launcher 肯定是需要执行 pause 的,但执行 pause 的时机 Launcher 自身无法控制,只能由 AMS 控制。

1.2 总体流程

在这里插入图片描述
这里有4个阶段,以4个颜色表示,同时还涉及到3个进程。

第一阶段:

    1. 由 Launcher 进程发起启动 Activity 的请求
    1. AMS 处理,创建对应的 ActivityRecord 和 Task ,并挂载到窗口层级树中
    1. AMS 触发 Launcher 的 pause 流程
    1. AMS 触发"电话"应用进程的创建

第二阶段:

    1. Launcher 执行完 pause
    1. pause 流程完成后触发 AMS 启动 TargetActivity

第三阶段:

    1. 创建应用进程
    1. 应用进程创建好后会向 AMS 进行绑定,并触发 AMS 启动 TargetActivity

第四阶段:

    1. AMS 触发 realStartActivityLocked 方法试图应用启动 Activity
    1. 如果还有 Activity 没有执行完 pause 逻辑,则 realStartActivityLocked 会 return
    1. 如果应用 Activity 都执行完了 pause ,则触发 TargetActivity 的启动,并将生命周期会执行到 onResume

注意:其中第2,3阶段几乎是同时开始的,并且 ASM 通知 Launcher 执行 pause 和通过 Zygote 创建进程是异步操作,不知道各自执行的顺序。

但是看的出来最终都是会执行到 realStartActivityLocked 方法来试图启动 TargetActivity ,为什么说是“试图启动”启动呢?

一共就有2种可能:

    1. completePause 先执行完
    1. 进程先创建完

假设第一种情况:
Launcher 先执行完 completePause 来到 ActivityTaskSupervisor::startSpecificActivity 方法,这个时候进程还没创建完毕,则不会执行 ActivityTaskSupervisor::realStartActivityLocked ,而是会再触发进程创建,当然之前已经触发过来,这次触发不会真的再创建进行了。

然后会由阶段三的流程创建好进程走到 ActivityTaskSupervisor::realStartActivityLocked ,在这个方法里会判断是不是执行完 pause 了,那当前这个情况肯定是满足的,
所以会触发启动 TargetActivity 。

假设第二种情况:
阶段三创建进程先执行完,走到 ActivityTaskSupervisor::realStartActivityLocked 方法,但是发现 pause 还没执行玩,所以就 return 了。
然后等 completePause 流程来到 ActivityTaskSupervisor::startSpecificActivity 方法时,这次发现进程已经创建好了,则一次执行后续逻辑触发启动 TargetActivity 。

也就是说不管阶段二,三谁先执行完都会试图启动 TargetActivity ,而成功启动必须有2个条件:

    1. 进程创建完毕
    1. launcher执行完pause

也就说说这2个阶段后执行过来的流程才能正式启动 TargetActivity 。

2. 阶段一:桌面点击图标启动应用

2.1 流程概览

这一阶段调用比较简单,堆栈如下:
在这里插入图片描述
整理后如下:

ItemClickHandler::onClickItemClickHandler::onClickAppShortcutItemClickHandler::startAppShortcutOrInfoActivityQuickstepLauncher::startActivitySafelyLauncher::startActivitySafelyAppLauncher::startActivitySafelyBaseQuickstepLauncher::getActivityLaunchOptions    -- 构建 Option 参数Activity::startActivity  -- 共用启动Activity流程Activity::startActivityActivity::startActivityActivity::startActivityForResultInstrumentation::execStartActivityActivityTaskManagerService::startActivity -- 跨进程

画成时序图:
在这里插入图片描述

其实我们正常通过 startActivity 传递 Intent 启动 Activity 的流程也是一样的,最终都会调到 Instrumentation::execStartActivity 然后开始跨进程与 AMS 通信。
只不过这边多了一些 Launcher 自己的处理,这边只需要对 BaseQuickstepLauncher::getActivityLaunchOptions 有个印象即可,这个方法会构建 ActivityOptions 对象,包含了一些启动参数,比如:远端动画的 RemoteAnimationAdapter 。

前面的流程就不看了,执行从 Activity::startActivityForResult 开始看代码流程。

2.2 SourceActivity 端处理

SourceActivity 发起启动 Activity 的逻辑相对简单,无论哪种参数的 Activity::startActivity 方法,最终都是调到 Activity::startActivityForResult 方法。
然后在 Instrumentation 最后的处理,最后跨进程传递到 system_service 进程中。

# Activity.javapublic void startActivityForResult(@RequiresPermission Intent intent, int requestCode,@Nullable Bundle options) {if (mParent == null) {options = transferSpringboardActivityOptions(options);Instrumentation.ActivityResult ar =mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, this,intent, requestCode, options);......}......}
# Instrumentation.javapublic ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target,Intent intent, int requestCode, Bundle options) {......// 当前应用进程处理结束,开始传递给 ActivityTaskManagerServiceint result = ActivityTaskManager.getService().startActivity(whoThread,who.getOpPackageName(), who.getAttributionTag(), intent,intent.resolveTypeIfNeeded(who.getContentResolver()), token,target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);// 对跨进程启动的结果做checkcheckStartActivityResult(result, intent);......}

Instrumentation::checkStartActivityResult 这个方法有一些常见的报错,比如常见的未在 AndroidManifest.xml 注册 Activity 的报错就在这。

# Instrumentationpublic static void checkStartActivityResult(int res, Object intent) {if (!ActivityManager.isStartResultFatalError(res)) {return;}switch (res) {case ActivityManager.START_INTENT_NOT_RESOLVED:case ActivityManager.START_CLASS_NOT_FOUND:  // 未在AndroidManifest.xml注册if (intent instanceof Intent && ((Intent)intent).getComponent() != null)throw new ActivityNotFoundException("Unable to find explicit activity class "+ ((Intent)intent).getComponent().toShortString()+ "; have you declared this activity in your AndroidManifest.xml"+ ", or does your intent not match its declared <intent-filter>?");throw new ActivityNotFoundException("No Activity found to handle " + intent);case ActivityManager.START_PERMISSION_DENIED:throw new SecurityException("Not allowed to start activity "+ intent);case ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:throw new AndroidRuntimeException("FORWARD_RESULT_FLAG used while also requesting a result");case ActivityManager.START_NOT_ACTIVITY:throw new IllegalArgumentException("PendingIntent is not an activity");case ActivityManager.START_NOT_VOICE_COMPATIBLE:throw new SecurityException("Starting under voice control not allowed for: " + intent);case ActivityManager.START_VOICE_NOT_ACTIVE_SESSION:throw new IllegalStateException("Session calling startVoiceActivity does not match active session");case ActivityManager.START_VOICE_HIDDEN_SESSION:throw new IllegalStateException("Cannot start voice activity on a hidden session");case ActivityManager.START_ASSISTANT_NOT_ACTIVE_SESSION:throw new IllegalStateException("Session calling startAssistantActivity does not match active session");case ActivityManager.START_ASSISTANT_HIDDEN_SESSION:throw new IllegalStateException("Cannot start assistant activity on a hidden session");case ActivityManager.START_CANCELED:throw new AndroidRuntimeException("Activity could not be started for "+ intent);default:throw new AndroidRuntimeException("Unknown error code "+ res + " when starting " + intent);}}

3. 阶段一 : system_service 处理

3.1 流程概述

对于 system_service 来说,它收到了启动 Activity 的请求,那么它要做的就是启动 TargetActivity 。

要实现这个目的,我整理了大概要做以下几件事:

    1. 解析请求参数
    • 想要启动 Activity 最起码得知道具体是哪个 Activity 吧,当然还有其他很多参数,比如是谁发起的请求,requestCode等等,这些参数都是应用端带过来的。system_service 的第一件事肯定是解析这些参数,这些参数将被解析存放在一个叫 Request 对象中。
    1. 处理窗口层级树相关逻辑
    • 虽然是启动 Activity ,但是肯定涉及到窗口操作,所以这一步也是必须的
    1. 触发 SourceActivity 执行pause 逻辑
    • 需要显示新的 TargetActivity ,那之前的 SourceActivity 肯定要执行 pause 逻辑的
    1. 触发创建进程
    • 当前场景是冷启动,那启动 TargetActivity 前必须要保证其所在的进程已经存在

在 Instrumentation::execStartActivity 里就开始触发跨进程通信了,剩下的逻辑就在 system_service 进程中执行了。

这部分缩略版的的调用链如下:

ActivityTaskManagerService::startActivityActivityTaskManagerService::startActivityAsUserActivityTaskManagerService::startActivityAsUserActivityStartController::obtainStarterActivityStarter::executeActivityStarter$Request::resolveActivity           -- 解析启动请求参数ActivityStarter::executeRequest                    -- 3.3 创建ActivityRecordActivityStarter::startActivityUncheckedActivityStarter::startActivityInner        -- 3.4 关键函数startActivityInnerActivityStarter::getOrCreateRootTask   -- 3.4.1 创建或者拿到Task                ActivityStarter::setNewTask            -- 3.4.2 将Task与activityRecord 绑定Task::moveToFront                       --3.4.3 移动Task到栈顶RootWindowContainer::resumeFocusedTasksTopActivities    --3.4.4 显示ActivityTask::resumeTopActivityUncheckedLockedTask::resumeTopActivityInnerLockedTaskFragment::resumeTopActivity      --  显示顶层ActivityTaskDisplayArea::pauseBackTasks                   -- pause LauncherActivity ActivityTaskManagerService::startProcessAsync     -- 创建“电话”进程

时序图:
在这里插入图片描述

流程来到 ActivityTaskManagerService::startActivity ,经过2次简单的跳转会执行 ActivityTaskManagerService::startActivityAsUser 方法。
这个方法比较重要,在这里会构建一个 ActivityStartController ,根据类名可以知道这个类是控制 Activity 启动。

3.2 启动请求参数的构建

代码如下:

# ActivityTaskManagerServiceprivate int startActivityAsUser(IApplicationThread caller, String callingPackage,@Nullable String callingFeatureId, Intent intent, String resolvedType,IBinder resultTo, String resultWho, int requestCode, int startFlags,ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {......// 返回的是ActivityStartControllerreturn getActivityStartController().obtainStarter(intent, "startActivityAsUser").setCaller(caller).setCallingPackage(callingPackage).setCallingFeatureId(callingFeatureId).setResolvedType(resolvedType).setResultTo(resultTo).setResultWho(resultWho).setRequestCode(requestCode).setStartFlags(startFlags).setProfilerInfo(profilerInfo).setActivityOptions(bOptions).setUserId(userId).execute();}ActivityStartController getActivityStartController() {return mActivityStartController;}

ActivityStartController::obtainStarter 返回的是 ActivityStarter 对象。

而 ActivityStarter 这个类名看着就是处理启动 Activity 的一个类,它做的是就是解析调用者传递的参数,构建出一个 Request 然后开始执行后续的启动 Activity 逻辑。

# ActivityStartController// 返回ActivityStarterActivityStarter obtainStarter(Intent intent, String reason) {return mFactory.obtain().setIntent(intent).setReason(reason);}

所以在 ActivityTaskManagerService::startActivityAsUser 方法中的 build 模式,其实是对 ActivityStarter 对象做构建,最终调用其 execute 方法。
在内容最终执行了 ActivityStarter::executeRequest 方法,下面只以 setCallingPackage 这一个方法看一下是怎么把参数设置到 Request 中的。

# ActivityStarter// 启动Activity的请求Request mRequest = new Request();// 设置调用者包名ActivityStarter setCallingPackage(String callingPackage) {mRequest.callingPackage = callingPackage;return this;}// 设置ActivityInfoActivityStarter setActivityInfo(ActivityInfo info) {mRequest.activityInfo = info;return this;}int execute() {......// 如果 mRequest 中没有 Activity 相关信息if (mRequest.activityInfo == null) {// 解析请求数据mRequest.resolveActivity(mSupervisor);}......// 执行启动Activity请求res = executeRequest(mRequest);......}

build 模式会把参数设置到 mRequest 中,但是并没有看到调用 ActivityStarter::setActivityInfo 所以在执行 ActivityStarter::execute 的时候 “mRequest.activityInfo”是 null 。
所以这里又分2步:

    1. 解析获取 activityInfo
    1. 执行后续启动逻辑

3.2.1 activityInfo 的解析

ActivityStarter$RequestResolveInfo resolveInfo;ActivityInfo activityInfo;void resolveActivity(ActivityTaskSupervisor supervisor) {......// 解析resolveInforesolveInfo = supervisor.resolveIntent(intent, resolvedType, userId,0 /* matchFlags */,computeResolveFilterUid(callingUid, realCallingUid, filterCallingUid),realCallingPid);......// 解析activityInfo activityInfo = supervisor.resolveActivity(intent, resolveInfo, startFlags,profilerInfo);......}

TargetActivity 所在的进程包名和 TargetActivity 的完整路径都在 activityInfo 中,这个解析方法也是一个核心点,当前分析主流程就不细看了。

现在 mRequest 中就包含了启动 TargetActivity 的所有数据,就可以继续后面的启动流程了。

3.3 创建ActivityRecord

ActivityStarter::executeRequest 是一个需要注意的方法,因为内部会创建 ActivityRecord 对象,而这个 ActivityRecord 对象持有 token ,这个 token 就是以后分析其他逻辑一直会出现的 token 。也可以说是 system_service 中一个 Activity 的唯一标识。

应用进程中的 Activity 在 AMS 的代表就是 ActivityRecord 。

# ActivityStarterprivate int executeRequest(Request request) {......// 包名在这ActivityInfo aInfo = request.activityInfo;ResolveInfo rInfo = request.resolveInfo;......if (err == ActivityManager.START_SUCCESS) {// 重点* 1. 打印创建 ActivityRecord 日志request.logMessage.append("START u").append(userId).append(" {").append(intent.toShortString(true, true, true, false)).append("} with ").append(launchModeToString(launchMode)).append(" from uid ").append(callingUid);if (callingUid != realCallingUid&& realCallingUid != Request.DEFAULT_REAL_CALLING_UID) {request.logMessage.append(" (realCallingUid=").append(realCallingUid).append(")");}}......// 重点* 2. 构造出一个ActivityRecordfinal ActivityRecord r = new ActivityRecord.Builder(mService).setCaller(callerApp).setLaunchedFromPid(callingPid).setLaunchedFromUid(callingUid).setLaunchedFromPackage(callingPackage).setLaunchedFromFeature(callingFeatureId).setIntent(intent).setResolvedType(resolvedType).setActivityInfo(aInfo).setConfiguration(mService.getGlobalConfiguration()).setResultTo(resultRecord).setResultWho(resultWho).setRequestCode(requestCode).setComponentSpecified(request.componentSpecified).setRootVoiceInteraction(voiceSession != null).setActivityOptions(checkedOptions).setSourceRecord(sourceRecord).build();......// 重点* 3. 继续执行startActivityUncheckedmLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,request.voiceInteractor, startFlags, true /* doResume */, checkedOptions,inTask, inTaskFragment, restrictedBgActivity, intentGrants);......}

3个注意点:

    1. 虽然是个日志,但是也很关键,一般分析日志看启动了哪个 Activity 就搜“START u” ,打印的地方就在这。另外发现 U 版本上这一块还做了改变。
    1. 构建出一个 ActivityRecord 对象
    1. 继续主流程
      主要看第二点 ActivityRecord 是怎么创建的。
# ActivityRecordprivate ActivityRecord(ActivityTaskManagerService _service, ......) {// 调用父类,创建 Tokensuper(_service.mWindowManager, new Token(), TYPE_APPLICATION, true,null /* displayContent */, false /* ownerCanManageAppTokens */);......packageName = info.applicationInfo.packageName;.....}

这里的重点其实就是创建了一个匿名 Token 然后传递到了父类的构建方法, ActivityRecord 的父类是 WindowToken 。

# WindowTokenprotected WindowToken(WindowManagerService service, IBinder _token, int type,boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens) {this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens,false /* roundedCornerOverlay */, false /* fromClientToken */, null /* options */);}protected WindowToken(WindowManagerService service, IBinder _token, int type,boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens,boolean roundedCornerOverlay, boolean fromClientToken, @Nullable Bundle options) {super(service);// token 赋值token = _token;windowType = type;mOptions = options;......if (dc != null) {// 挂载到窗口树,dc.addWindowToken(token, this);}}

将 ActivityRecord 创建的匿名 保存在了 token 对象,这个 token 就是 Activity 在 system_service 里唯一标识符。

普通的 WindowToken 在这里会挂载的窗口层级树,但是 DisplayContent::addWindowToken 方法内部对 ActivityRecord 做了判断。也就是说 ActivityRecord 刚创建好后是不会挂载到窗口树上的,当前流程后面是要介绍 ActivityRecord 是怎么挂载到层级树上的。

ActivityRecord 创建好后继续走主流程,根据前面的分析下一步是执行 ActivityStarter::startActivityUnchecked 不过这个方法也没做啥,主要是调用了 ActivityStarter::startActivityInner ,这个方法是流程的关键点。

3.4 窗口层级树处理

ActivityStarter::startActivityInner 是 Activity 启动流程最重要的函数之一,这里涉及到【窗口层级树】相关知识,上一小节说了 ActivityRecord 创建好后并不会和 WindowToken 一样挂载到层级树中,而是需要单独处理,本小节就是看这一块是如何处理的。

先看看正常在 Launcher 界面时和启动“电话”后的层级树对比。

在这里插入图片描述

这里首先多了3个东西:

    1. Task
    1. 上一步创建的 ActivityRecord
    1. WindowState。

另外这个 Task 还移动到了DefaultTaskDisplayArea的最顶部,这里涉及到的操作如下:

    1. 创建 Task
    1. ActivityRecord 挂在到这个 Task 下
    1. 将这个 Task 移动到最上面

至于最下面的那个 546fce2 为什么是 WindowState 对象,又是怎么挂在到 ActivityRecord 上的,这个属于窗口显示第一步-addWindow流程当前就不单独分析了。

继续看主流程代码:

# ActivityStarter// The task that the last activity was started into. We currently reset the actual start// activity's task and as a result may not have a reference to the task in all casesprivate Task mTargetTask;int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,int startFlags, boolean doResume, ActivityOptions options, Task inTask,TaskFragment inTaskFragment, boolean restrictedBgActivity,NeededUriGrants intentGrants) {......// computeTargetTask内部会根据具体条件返回Task(比如标志位FLAG_ACTIVITY_NEW_TASK 就需要重新创建Task   )// 这里reusedTask为 null,因为是新启动的应用,所以computeTargetTask也找不到task,最终也为nullfinal Task targetTask = reusedTask != null ? reusedTask : computeTargetTask();// 那么newTask为true, 表示需要新建一个taskfinal boolean newTask = targetTask == null;// 同样为nullmTargetTask = targetTask;......if (mTargetRootTask == null) {// 重点* 1. 创建Task   23mTargetRootTask = getOrCreateRootTask(mStartActivity, mLaunchFlags, targetTask,mOptions);}if (newTask) {// taskToAffiliate 为nullfinal Task taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)? mSourceRecord.getTask() : null;// 重点* 2. 将需要启动的ActivityRecord与 新创建的Task 进行绑定setNewTask(taskToAffiliate);} else if (mAddingToTask) {addOrReparentStartingActivity(targetTask, "adding to task");}if (!mAvoidMoveToFront && mDoResume) {// 重点* 3. 移动到栈顶 mTargetRootTask.getRootTask().moveToFront("reuseOrNewTask", targetTask);......}......if (mDoResume) {......// 重点*4. task 处理完后,需要将task顶部的Activity显示(resume) // mTransientLaunch 一般为默认值 falsemRootWindowContainer.resumeFocusedTasksTopActivities(mTargetRootTask, mStartActivity, mOptions, mTransientLaunch);}......}
    1. 创建 Task
    • 我们知道 ActivityRecord 需要挂载到一个 Task 下,虽然这里调用的方法名是 getOrCreateRootTask(获取或者创建Task),但是当前场景肯定是创建一个,热启动是获取。
    • 另外 Task 创建的时候就会挂载到层级树中,也就是会挂载到DefaultTaskDisplayArea
    1. 将 ActivityRecord 挂载到创建的 Task 下
    • 上一小节没做的事,在这里就完成了挂载
    1. 移动 Task 到栈顶
    • 因为这个新启动的 Activity 需要显示,所以需要将它所在的 Task 移到容器顶部。 不过当前场景建好就已经是栈顶了,其实这一步没做什么,这点后面代码会分析
    1. 显示顶部的 Activity
    • 窗口层级的处理好了,就可以处理显示逻辑了,这里后续的逻辑会触发 Activity 的启动

3.4.1 获取Task–getOrCreateRootTask

调用链

ActivityStarter::getOrCreateRootTaskRootWindowContainer::getOrCreateRootTaskRootWindowContainer::getOrCreateRootTaskTaskDisplayArea::getOrCreateRootTaskTaskDisplayArea::getOrCreateRootTaskTask::Build     ---创建Task

开始看代码:

# ActivityStarterprivate Task getOrCreateRootTask(ActivityRecord r, int launchFlags, Task task,ActivityOptions aOptions) {final boolean onTop =(aOptions == null || !aOptions.getAvoidMoveToFront()) && !mLaunchTaskBehind;final Task sourceTask = mSourceRecord != null ? mSourceRecord.getTask() : null;return mRootWindowContainer.getOrCreateRootTask(r, aOptions, task, sourceTask, onTop,mLaunchParams, launchFlags);}
    1. onTop 表示是否要移到到当前栈顶,那肯定是要的,新启动的 Activity 当前要在最上面,这里 aOptions 为 null ,所以为 true
    1. sourceTask 表示从哪里启动的,当前 Launcher 所在的 Task 就是 sourceTask

然后开始获取一个 Task 如果没有就新建一个。

# RootWindowContainerTask getOrCreateRootTask(@Nullable ActivityRecord r, @Nullable ActivityOptions options,@Nullable Task candidateTask, boolean onTop) {return getOrCreateRootTask(r, options, candidateTask, null /* sourceTask */, onTop,null /* launchParams */, 0 /* launchFlags */);}Task getOrCreateRootTask(@Nullable ActivityRecord r,@Nullable ActivityOptions options, @Nullable Task candidateTask,@Nullable Task sourceTask, boolean onTop,@Nullable LaunchParamsController.LaunchParams launchParams, int launchFlags) {......final int activityType = resolveActivityType(r, options, candidateTask);if (taskDisplayArea != null) {if (canLaunchOnDisplay(r, taskDisplayArea.getDisplayId())) {// 重点*1. 传递到TaskDisplayAreareturn taskDisplayArea.getOrCreateRootTask(r, options, candidateTask,sourceTask, launchParams, launchFlags, activityType, onTop);} else {taskDisplayArea = null;}}......}

经过同名方法调用后,逻辑进入到 TaskDisplayArea::getOrCreateRootTask 。

# TaskDisplayAreaTask getOrCreateRootTask(int windowingMode, int activityType, boolean onTop,@Nullable Task candidateTask, @Nullable Task sourceTask,@Nullable ActivityOptions options, int launchFlags) {if(....) {// 拿到之前创建的Taskreturn candidateTask.getRootTask();}......// 第一次显示所以是新建Taskreturn new Task.Builder(mAtmService).setWindowingMode(windowingMode).setActivityType(activityType).setOnTop(onTop).setParent(this)  // 主要这个this被设置为Parent。所以直接挂载到了DefaultTaskDisplayArea下.setSourceTask(sourceTask).setActivityOptions(options).setLaunchFlags(launchFlags).build();}

看方法名是获取或创建 Task ,当前流程是新启动的 Activity 所以需要创建 Task 。如果是以默认启动方式打开应用内的另一个 Activity ,就走的是上面的 “return candidateTask.getRootTask();”

设置的 parent 就是层级结构树应用所在的名为“DefaultTaskDisplayArea”的 TaskDisplayArea

接下来就是真正触发Task的创建。

# Task# Task.BuilderTask build() {if (mParent != null && mParent instanceof TaskDisplayArea) {validateRootTask((TaskDisplayArea) mParent);}......// 重点* 1. 创建taskfinal Task task = buildInner();task.mHasBeenVisible = mHasBeenVisible;// Set activity type before adding the root task to TaskDisplayArea, so home task can// be cached, see TaskDisplayArea#addRootTaskReferenceIfNeeded().if (mActivityType != ACTIVITY_TYPE_UNDEFINED) {task.setActivityType(mActivityType);}// 重点* 2. 入栈 这里的 mOnTop 为trueif (mParent != null) {if (mParent instanceof Task) {final Task parentTask = (Task) mParent;parentTask.addChild(task, mOnTop ? POSITION_TOP : POSITION_BOTTOM,(mActivityInfo.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);} else {mParent.addChild(task, mOnTop ? POSITION_TOP : POSITION_BOTTOM);}}// Set windowing mode after attached to display area or it abort silently.if (mWindowingMode != WINDOWING_MODE_UNDEFINED) {task.setWindowingMode(mWindowingMode, true /* creating */);}// 返回return task;}// 创建Task buildInner() {return new Task(mAtmService, mTaskId, mIntent, mAffinityIntent, mAffinity,mRootAffinity, mRealActivity, mOrigActivity, mRootWasReset, mAutoRemoveRecents,mAskedCompatMode, mUserId, mEffectiveUid, mLastDescription, mLastTimeMoved,mNeverRelinquishIdentity, mLastTaskDescription, mLastSnapshotData,mTaskAffiliation, mPrevAffiliateTaskId, mNextAffiliateTaskId, mCallingUid,mCallingPackage, mCallingFeatureId, mResizeMode, mSupportsPictureInPicture,mRealActivitySuspended, mUserSetupComplete, mMinWidth, mMinHeight,mActivityInfo, mVoiceSession, mVoiceInteractor, mCreatedByOrganizer,mLaunchCookie, mDeferTaskAppear, mRemoveWithTaskOrganizer);}
3.4.1.1 小结

最后描述一下最后创建的2个重点部分:

    1. 看到通过 buildInner 创建了一个 Task,而 buildInner 也很简单粗暴,通过各个变量直接 new 出一个 Task 对象。
    1. mParent 不为 null ,是因为在创建的时候 setParent(this),当前的这个 this 就是 getDefaultTaskDisplayArea 返回的。就是 37层的第二层应用 Activity 存在的"DefaultTaskDisplayArea"

在 RootWindowContainer::getOrCreateRootTask 体现。

在这里插入图片描述
注意 log 里的 #17 的这个 Task 与前面的层级结构树新增的 Task 是对应的上的。而且"this= DefaultTaskDisplayArea "说明也确实是往 DefaultTaskDisplayArea 里添加了。
另外 log 里 index 为3,结合最上面的前后对比,说明也的往顶部添加。

3.4.2 ActivityRecord挂载到Task–setNewTask

调用链

    ActivityStarter::setNewTaskActivityStarer::addOrReparentStartingActivity

主流程代码

# ActivityStarerprivate void setNewTask(Task taskToAffiliate) {// 为truefinal boolean toTop = !mLaunchTaskBehind && !mAvoidMoveToFront;// 就是mTargetRootTask,也就是刚刚创建的Taskfinal Task task = mTargetRootTask.reuseOrCreateTask(mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession,mVoiceInteractor, toTop, mStartActivity, mSourceRecord, mOptions);task.mTransitionController.collectExistenceChange(task);// ActivityRecord的挂载addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask");// 需要注意这里的日志打印ProtoLog.v(WM_DEBUG_TASKS, "Starting new activity %s in new task %s",mStartActivity, mStartActivity.getTask());// mLaunchTaskBehind 为false,所以taskToAffiliate 为null if (taskToAffiliate != null) {mStartActivity.setTaskToAffiliateWith(taskToAffiliate);}}

这里的 Task 和 mTargetRootTask 是同一个对象,然后进入 ActivityStarer::addOrReparentStartingActivity 。

# ActivityStarerprivate void addOrReparentStartingActivity(@NonNull Task task, String reason) {//  newParent = task 都是刚刚创建的TaskTaskFragment newParent = task;......if (mStartActivity.getTaskFragment() == null|| mStartActivity.getTaskFragment() == newParent) {// 重点, 将 ActivityRecord挂在到新创建的Task中,并且是顶部newParent.addChild(mStartActivity, POSITION_TOP);} else {mStartActivity.reparent(newParent, newParent.getChildCount() /* top */, reason);}}

这里的逻辑涉及到的 Task 就是上一步创建的 Task ,而 mStartActivity 则是“电话”在之前逻辑创建的 ActivityRecord 。

setNewTask的堆栈信息如下

在这里插入图片描述

另外这段逻辑里有个 ProtoLog 打印,日志如下:

在这里插入图片描述

3.4.2.1 小结

结合逻辑分析 + 堆栈信息 + ProtoLog ,可以确认 ActivityStarer::setNewTask 做的事情就是将 ActivityRecord 挂在到 Task 中,而且在顶部。

3.4.3 移动Task到容器顶部–moveToFront

这里提一下这个 moveToFront 方法,因为前面创建 Task 并添加到 DefaultTaskDisplayArea 时是往顶部添加,后面将 ActivityRecord 挂在到 Task 也是挂在到其顶部。所以这个函数其实没有什么实际操作。但是对于其他场景,这里也是一个重点方法。

调用链

Task::moveToFrontTask::moveToFrontInnerTaskDisplayArea::positionChildAtTaskDisplayArea::positionChildTaskAtActivityTaskSupervisor::updateTopResumedActivityIfNeededActivityRecord::onTopResumedActivityChanged      --触发TopResumedActivityChangeItem

主流程代码

首先确定一个问题:需要移动到顶部的是哪个 Task ? 这个 Task 所在的是在哪个哪个容器?
在 ActivityStarter::startActivityInner 的时候调用的是这段代码:

// targetTask 当前场景为null
mTargetRootTask.getRootTask().moveToFront("reuseOrNewTask", targetTask);

已知 mTargetRootTask 是新创建给“电话”用的 Task, 而 mTargetRootTask.getRootTask() 返回值当前场景是 mTargetRootTask 本身。

# Taskvoid moveToFront(String reason, Task task) {if (!isAttached()) {return;}mTransitionController.recordTaskOrder(this);final TaskDisplayArea taskDisplayArea = getDisplayArea();if (!isActivityTypeHome() && returnsToHomeRootTask()) {// Make sure the root home task is behind this root task since that is where we// should return to when this root task is no longer visible.taskDisplayArea.moveHomeRootTaskToFront(reason + " returnToHome");}final Task lastFocusedTask = isRootTask() ? taskDisplayArea.getFocusedRootTask() : null;if (task == null) {// 当前场景为null,所以会赋值成新建的 Task ,也就是 mTargetRootTask 也就是电话的 Tasktask = this;}// 这里调用 getParent 就是 DefaultTaskDisplayArea 了。// 把当前的 Task 移动到 DefaultTaskDisplayArea 的最前面task.getParent().positionChildAt(POSITION_TOP, task, true /* includingParents */);taskDisplayArea.updateLastFocusedRootTask(lastFocusedTask, reason);}

再回答一下问题:移动的是新建的电话 Task ,它的父容器是 DefaultTaskDisplayArea ,所以是把这个 Task 移动到 DefaultTaskDisplayArea 的最前面。

mTargetRootTask.getRootTask 返回的是顶部的 Task, 当前 Task 上一层是 TaskDisplayArea 类型 (name为DefaultTaskDisplayArea)

而 mTargetRootTask.getParent 返回的父容器(不限制是 Task),则是 name 为 DefaultTaskDisplayArea 的 TaskDisplayArea。

getRootTask 和 getParent 的区别可以自行去源码中看看

到这里,AMS已经将需要的 ActivityRecord 和 Task 创建并且挂载到层级树中,接下来将是需要处理 TargetActivity 启动和显示逻辑了

3.4.4 显示Activity–resumeFocusedTasksTopActivities

首先看方法名目的需要显示一个栈顶的 Activity ,那说的不就是 TargetActivity 嘛,TargetActivity 的 ActivityRecord 已经创建并且移动到栈顶了。
前面该做的也都做好了,现在也确实是时候处理显示逻辑,这部分流程由 RootWindowContainer::resumeFocusedTasksTopActivities 执行,调用链如下:

RootWindowContainer::resumeFocusedTasksTopActivitiesTask::resumeTopActivityUncheckedLockedTask::resumeTopActivityInnerLockedTaskFragment::resumeTopActivity TaskDisplayArea::pauseBackTasks  --   pause LauncherActivity WindowContainer::forAllLeafTaskTaskFragment::forAllLeafTaskFragmentsTaskFragment::startPausingTaskFragment::startPausingTaskFragment::schedulePauseActivity --构建 PauseActivityItem,这里是触发暂停launchActivityTaskManagerService::startProcessAsync     -- 创建“电话”进程

重点方法在 TaskFragment::resumeTopActivity 开始处理,先简单看一下前面的调用是怎么样的。

# RootWindowContainer// 第四个参数为 falseboolean resumeFocusedTasksTopActivities(Task targetRootTask, ActivityRecord target, ActivityOptions targetOptions,boolean deferPause) {......if (targetRootTask != null && (targetRootTask.isTopRootTaskInDisplayArea()|| getTopDisplayFocusedRootTask() == targetRootTask)) {result = targetRootTask.resumeTopActivityUncheckedLocked(target, targetOptions,deferPause);}......}
# Taskboolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options,boolean deferPause) {......if (isLeafTask()) {if (isFocusableAndVisible()) {someActivityResumed = resumeTopActivityInnerLocked(prev, options, deferPause);}}......}private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options,boolean deferPause) {......// deferPause = falseresumed[0] = topFragment.resumeTopActivity(prev, options, deferPause);......}

由于篇幅原因,下面的 TaskFragment::resumeTopActivity 流程放在下一篇。


http://www.ppmy.cn/server/125983.html

相关文章

SpringBoot项目 | 瑞吉外卖 | 短信发送验证码功能改为免费的邮箱发送验证码功能 | 代码实现

0.前情提要 之前的po已经说了单独的邮箱验证码发送功能怎么实现&#xff1a; https://blog.csdn.net/qq_61551948/article/details/142641495 这篇说下如何把该功能整合到瑞吉项目里面&#xff0c;也就是把原先项目里的短信发送验证码的功能改掉&#xff0c;改为邮箱发送验证…

LeetCode讲解篇之3. 无重复字符的最长子串

文章目录 题目描述题解思路代码实现 题目描述 题解思路 因为我们需要求无重复字符的最长子串&#xff0c;这个我们首先需要想到使用滑动窗口&#xff0c;窗口内记录无重复的子串的所有字符&#xff0c;移动窗口的右边界时&#xff0c;发现当前字符在窗口内已经出现&#xff0c…

.Net 6.0 Windows平台如何判断当前电脑是否联网

最近在工作中开发需要判断当前电脑是否联网的需求&#xff0c;在网上找了一个调用window API来判断本机是否联网。具体请看下面介绍&#xff1a; 1.方法一&#xff08;调用winAPI&#xff09; [DllImport("wininet")] public static extern bool InternetGetConnec…

遗传算法与深度学习实战(15)——差分进化详解与实现

遗传算法与深度学习实战&#xff08;15&#xff09;——差分进化详解与实现 0. 前言1. 差分进化1.1 基本原理1.2 差分进化基本流程 2. 使用差分进化逼近复杂和不连续函数小结系列链接 0. 前言 深度学习 (Deep learning, DL) 系统通常可以被简单的视为凸函数逼近器&#xff0c;…

基于大数据的大屏高速公路收费系统的开发设计与实现SpringBoot+vue

目录 1. 需求分析 2. 技术选型 3. 系统架构设计 4. 开发实现 5. 代码示例和效果演示 6. 持续优化 由于我国高速公路的建设和发展与国外先进国家有很大差距。在高速公路建成后&#xff0c;收费系统往往选用国外的成熟产品。虽然这些产品在功能上基本满足了高速公路收费的要…

el-table初始化时根据传入数据选中某些行

el-table初始化时根据传入数据选中某些行 // 父组件 const mockTableData ref<TableData[]>([{key: 111,value: Tom111,description: No. 189, Grove St, Los Angeles,selected: true,}]);# 子组件 <el-table:data"TableData"borderclass"edit-table…

在Linux实时监控某个应用是否运行,未运行,执行运行命令

1、shell脚本(每隔30秒检测一次) 脚本要注意的地方是&#xff1a;在Nodepad编辑的时候要使用Unix&#xff08;LF&#xff09;格式&#xff0c;避免在Linux无法执行命令 #!/bin/bash# RabbitMQ进程名称&#xff08;可能需要根据你的安装进行调整&#xff09; RABBITMQ_PROCE…

Java.反射

目录 1.获取class 的三种方式 2.利用反射获取构造方法 3.利用反射获取成员变量 4.利用反射获取成员方法 1.获取class 的三种方式 全类名怎么找? 全类名报名&#xff0b;类名 package MyReflect;public class Student {private String id;private String name;private int…