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

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

忽然有一天,我想要做一件事:去代码中去验证那些曾经被“灌输”的理论。
                  
                  
                                           – 服装学院的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 流程放在下一篇。

相关文章:

  • 大数据复习知识点5
  • linux服务器部署filebeat
  • [Everything] 文件搜索工具的下载及详细安装使用过程(附有下载文件)
  • Hadoop三大组件之HDFS(一)
  • 在树莓派上部署开源监控系统 ZoneMinder
  • 基于php的幸运舞蹈课程工作室管理系统
  • 黑名单与ip禁令是同一个东西吗
  • Android开发中的ViewModel
  • SpringMVC4-SpringMVC获取请求参数
  • TI DSP TMS320F280025 Note14:模数转换器ADC原理分析与应用
  • MapReduce学习与理解
  • StopWath,apache commons lang3 包下的一个任务执行时间监视器的使用
  • HTML5实现唐朝服饰网站模板源码
  • 服务器使用了代理ip,遇到流量攻击,会对服务器有影响吗
  • 汽车保养BBBBBBBBBBB
  • 07.Android之多媒体问题
  • 30秒的PHP代码片段(1)数组 - Array
  • Android单元测试 - 几个重要问题
  • Angular 2 DI - IoC DI - 1
  • classpath对获取配置文件的影响
  • gulp 教程
  • HashMap剖析之内部结构
  • Java Agent 学习笔记
  • JS基础篇--通过JS生成由字母与数字组合的随机字符串
  • python_bomb----数据类型总结
  • vue总结
  • Vue组件定义
  • 笨办法学C 练习34:动态数组
  • 从0搭建SpringBoot的HelloWorld -- Java版本
  • 分布式熔断降级平台aegis
  • 和 || 运算
  • 看完九篇字体系列的文章,你还觉得我是在说字体?
  • HanLP分词命名实体提取详解
  • JavaScript 新语法详解:Class 的私有属性与私有方法 ...
  • ​埃文科技受邀出席2024 “数据要素×”生态大会​
  • ​七周四次课(5月9日)iptables filter表案例、iptables nat表应用
  • #VERDI# 关于如何查看FSM状态机的方法
  • (20050108)又读《平凡的世界》
  • (31)对象的克隆
  • (C#)一个最简单的链表类
  • (C语言)球球大作战
  • (C语言)输入一个序列,判断是否为奇偶交叉数
  • (Java入门)抽象类,接口,内部类
  • (待修改)PyG安装步骤
  • (附源码)计算机毕业设计SSM疫情下的学生出入管理系统
  • (函数)颠倒字符串顺序(C语言)
  • (简单有案例)前端实现主题切换、动态换肤的两种简单方式
  • (五)activiti-modeler 编辑器初步优化
  • (一)基于IDEA的JAVA基础1
  • (转)自己动手搭建Nginx+memcache+xdebug+php运行环境绿色版 For windows版
  • .Net Core/.Net6/.Net8 ,启动配置/Program.cs 配置
  • .NET Standard 的管理策略
  • .NET Windows:删除文件夹后立即判断,有可能依然存在
  • .vimrc php,修改home目录下的.vimrc文件,vim配置php高亮显示
  • @ohos.systemParameterEnhance系统参数接口调用:控制设备硬件(执行shell命令方式)