Android 窗口结构(三) Home Task 添加Home ActivityRecord
Android系统起来之后,会启动Launcher的主Activity。
看一下调用链
SystemServer startOtherServices(……)
--> ActivityManagerService systemReady(……)
--> LocalService startHomeOnAllDisplays(……) LocalService是ActivityTaskManagerService 类的内部类
-> RootWindowContainer startHomeOnAllDisplays(……)
-> RootWindowContainer startHomeOnAllDisplays(int userId, String reason, int displayId)
--> RootWindowContainer startHomeOnAllDisplays(int userId, String reason, int displayId, boolean allowInstrumenting, boolean fromHomeKey)
--> DisplayContent reduceOnAllTaskDisplayAreas((taskDisplayArea, result) ->
result | startHomeOnTaskDisplayArea(userId, reason, taskDisplayArea, allowInstrumenting, fromHomeKey),
false /* initValue */)
--> RootWindowContainer startHomeOnTaskDisplayArea(int userId, String reason, TaskDisplayArea taskDisplayArea, boolean allowInstrumenting, boolean fromHomeKey)
--> ActivityStartController. startHomeActivity(……)
--> ActivityStarter execute()
这里直接从RootWindowContainer startHomeOnTaskDisplayArea(int userId, String reason, TaskDisplayArea taskDisplayArea, boolean allowInstrumenting, boolean fromHomeKey)看,代码如下:
boolean startHomeOnTaskDisplayArea(int userId, String reason, TaskDisplayArea taskDisplayArea,boolean allowInstrumenting, boolean fromHomeKey) {// Fallback to top focused display area if the provided one is invalid.if (taskDisplayArea == null) {final Task rootTask = getTopDisplayFocusedRootTask();taskDisplayArea = rootTask != null ? rootTask.getDisplayArea(): getDefaultTaskDisplayArea();}Intent homeIntent = null;ActivityInfo aInfo = null;if (taskDisplayArea == getDefaultTaskDisplayArea()) {homeIntent = mService.getHomeIntent();aInfo = resolveHomeActivity(userId, homeIntent);} else if (shouldPlaceSecondaryHomeOnDisplayArea(taskDisplayArea)) {Pair<ActivityInfo, Intent> info = resolveSecondaryHomeActivity(userId, taskDisplayArea);aInfo = info.first;homeIntent = info.second;}if (aInfo == null || homeIntent == null) {return false;}if (!canStartHomeOnDisplayArea(aInfo, taskDisplayArea, allowInstrumenting)) {return false;}// Updates the home component of the intent.homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK);// Updates the extra information of the intent.if (fromHomeKey) {homeIntent.putExtra(WindowManagerPolicy.EXTRA_FROM_HOME_KEY, true);if (mWindowManager.getRecentsAnimationController() != null) {mWindowManager.getRecentsAnimationController().cancelAnimationForHomeStart();}}homeIntent.putExtra(WindowManagerPolicy.EXTRA_START_REASON, reason);// Update the reason for ANR debugging to verify if the user activity is the one that// actually launched.final String myReason = reason + ":" + userId + ":" + UserHandle.getUserId(aInfo.applicationInfo.uid) + ":" + taskDisplayArea.getDisplayId();mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,taskDisplayArea);return true;}
taskDisplayArea是不为null的,它的构建可以参考看一下这篇文章 Android 窗口结构(一) 窗口层级构造。
Android是支持多屏幕的,这里咱们针对单屏幕来说。也即是满足第一个if分支的情况。下面是得到homeIntent,然后通过homeIntent再得到aInfo。
这里mService是ActivityTaskManagerService对象,它的getHomeIntent()得到的Intent对象,action为Intent.ACTION_MAIN,Category为Intent.CATEGORY_HOME。这个是Launcher的主Activity配置。
接着通过resolveHomeActivity(userId, homeIntent)解析该Intent对象,得到对应的Activity信息对象aInfo。这块如果只存在一个Launcher,它就返回对应Launcher主Activity的信息;如果是配置了多个Launcher,它会返回ResolverActivity的信息,如果是这样,等下面启动ResolverActivity时,它会显示一个界面 ,让选择运行哪个Launcher。这里咱们就说一个Launcher的情况。
接下来,它会配置Intent对象的ComponentName。它包括包名和Activity名字。还会加上FLAG_ACTIVITY_NEW_TASK标识,它代表启动一个新的Task。
再接下来就是调用ActivityStartController类对象startHomeActivity()方法去启动Activity。mService.getActivityStartController()是ActivityTaskManagerService对象的成员mActivityStartController,它是ActivityStartController类型。
ActivityStartController类的startHomeActivity方法如下:
void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason,TaskDisplayArea taskDisplayArea) {final ActivityOptions options = ActivityOptions.makeBasic();options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);if (!ActivityRecord.isResolverActivity(aInfo.name)) {// The resolver activity shouldn't be put in root home task because when the// foreground is standard type activity, the resolver activity should be put on the// top of current foreground instead of bring root home task to front.options.setLaunchActivityType(ACTIVITY_TYPE_HOME);}final int displayId = taskDisplayArea.getDisplayId();options.setLaunchDisplayId(displayId);options.setLaunchTaskDisplayArea(taskDisplayArea.mRemoteToken.toWindowContainerToken());// The home activity will be started later, defer resuming to avoid unnecessary operations// (e.g. start home recursively) when creating root home task.mSupervisor.beginDeferResume();final Task rootHomeTask;try {// Make sure root home task exists on display area.rootHomeTask = taskDisplayArea.getOrCreateRootHomeTask(ON_TOP);} finally {mSupervisor.endDeferResume();}mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason).setOutActivity(tmpOutRecord).setCallingUid(0).setActivityInfo(aInfo).setActivityOptions(options.toBundle()).execute();mLastHomeActivityStartRecord = tmpOutRecord[0];if (rootHomeTask.mInResumeTopActivity) {// If we are in resume section already, home activity will be initialized, but not// resumed (to avoid recursive resume) and will stay that way until something pokes it// again. We need to schedule another resume.mSupervisor.scheduleResumeTopActivities();}}
首先设置参数ActivityOptions类型options,先设置窗口模式全屏(WINDOWING_MODE_FULLSCREEN),如果启动的Activity不是ResolverActivity,设置启动Activty类型为ACTIVITY_TYPE_HOME,设置显示屏Id。
taskDisplayArea.mRemoteToken是RemoteToken类型对象,它继承自IWindowContainerToken.Stub,用来实现Binder通信。taskDisplayArea.mRemoteToken.toWindowContainerToken()则是WindowContainerToken(实现Parcelable接口,用来序列化)对象。这里也将它设置到options中。
再接下来就从taskDisplayArea中获取RootHomeTask。因为RootHomeTask的初始化在DisplayContent初始化中,DisplayContent初始化是在RootWindowContainer的setWindowManager(WindowManagerService wm)中,它又在ActivityManagerService的setWindowManager(WindowManagerService wm)中,所以现在它已经创建了。创建的过程参考 Android 窗口结构(二) 添加Home Task。
obtainStarter(intent, "startHomeActivity: " + reason)得到的是ActivityStarter对象,接着会向其设置intent对象,还会调用setCallingUid(0)、setActivityInfo(aInfo)、setActivityOptions(options.toBundle())分别设置调用Uid,启动的Activity信息、设置参数。最后调用ActivityStarter对象的execute()方法,执行。
tmpOutRecord是一个数组,是一个输出参数,所以上述方法调用完毕,之后,将它设置到mLastHomeActivityStartRecord中,作为最后一次启动的Home Activity。
如果rootHomeTask.mInResumeTopActivity为true,代表Home Task已经在执行恢复Activity的操作了,导致现在的Home Activity没有执行resume。所以这里接着调用mSupervisor.scheduleResumeTopActivities()继续执行Home Activity的resume。
下面进入ActivityStarter类中看一下它的execute()方法,如下:
/*** Resolve necessary information according the request parameters provided earlier, and execute* the request which begin the journey of starting an activity.* @return The starter result.*/int execute() {try {// Refuse possible leaked file descriptorsif (mRequest.intent != null && mRequest.intent.hasFileDescriptors()) {throw new IllegalArgumentException("File descriptors passed in Intent");}final LaunchingState launchingState;synchronized (mService.mGlobalLock) {final ActivityRecord caller = ActivityRecord.forTokenLocked(mRequest.resultTo);final int callingUid = mRequest.realCallingUid == Request.DEFAULT_REAL_CALLING_UID? Binder.getCallingUid() : mRequest.realCallingUid;launchingState = mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(mRequest.intent, caller, callingUid);}// If the caller hasn't already resolved the activity, we're willing// to do so here. If the caller is already holding the WM lock here,// and we need to check dynamic Uri permissions, then we're forced// to assume those permissions are denied to avoid deadlocking.if (mRequest.activityInfo == null) {mRequest.resolveActivity(mSupervisor);}// Add checkpoint for this shutdown or reboot attempt, so we can record the original// intent action and package name.if (mRequest.intent != null) {String intentAction = mRequest.intent.getAction();String callingPackage = mRequest.callingPackage;if (intentAction != null && callingPackage != null&& (Intent.ACTION_REQUEST_SHUTDOWN.equals(intentAction)|| Intent.ACTION_SHUTDOWN.equals(intentAction)|| Intent.ACTION_REBOOT.equals(intentAction))) {ShutdownCheckPoints.recordCheckPoint(intentAction, callingPackage, null);}}int res;synchronized (mService.mGlobalLock) {final boolean globalConfigWillChange = mRequest.globalConfig != null&& mService.getGlobalConfiguration().diff(mRequest.globalConfig) != 0;final Task rootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();if (rootTask != null) {rootTask.mConfigWillChange = globalConfigWillChange;}ProtoLog.v(WM_DEBUG_CONFIGURATION, "Starting activity when config "+ "will change = %b", globalConfigWillChange);final long origId = Binder.clearCallingIdentity();res = resolveToHeavyWeightSwitcherIfNeeded();if (res != START_SUCCESS) {return res;}res = executeRequest(mRequest);Binder.restoreCallingIdentity(origId);if (globalConfigWillChange) {// If the caller also wants to switch to a new configuration, do so now.// This allows a clean switch, as we are waiting for the current activity// to pause (so we will not destroy it), and have not yet started the// next activity.mService.mAmInternal.enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,"updateConfiguration()");if (rootTask != null) {rootTask.mConfigWillChange = false;}ProtoLog.v(WM_DEBUG_CONFIGURATION,"Updating to new configuration after starting activity.");mService.updateConfigurationLocked(mRequest.globalConfig, null, false);}// The original options may have additional info about metrics. The mOptions is not// used here because it may be cleared in setTargetRootTaskIfNeeded.final ActivityOptions originalOptions = mRequest.activityOptions != null? mRequest.activityOptions.getOriginalOptions() : null;// If the new record is the one that started, a new activity has created.final boolean newActivityCreated = mStartActivity == mLastStartActivityRecord;// Notify ActivityMetricsLogger that the activity has launched.// ActivityMetricsLogger will then wait for the windows to be drawn and populate// WaitResult.mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState, res,newActivityCreated, mLastStartActivityRecord, originalOptions);if (mRequest.waitResult != null) {mRequest.waitResult.result = res;res = waitResultIfNeeded(mRequest.waitResult, mLastStartActivityRecord,launchingState);}return getExternalResult(res);}} finally {onExecutionComplete();}}
如果Intent中含有文件描述符,是会抛出IllegalArgumentException异常的。
mSupervisor.getActivityMetricsLogger()是ActivityMetricsLogger对象,这个类是用来监听Activity启动、过渡、可见性改变、窗口绘制进行回调的。它的notifyActivityLaunching()就是通知Activity启动了。在这里它会记录启动的时间。
如果mRequest.activityInfo == null,会需要解析它,这里前面已经设置它了,不需要解析它。
如果intentAction和调用的包名callingPackage不为null,并且是关机或者重启的请求,会添加检查点。
变量globalConfigWillChange是全局的配置是否发生了变化,是用mRequest.globalConfig(不能为null)和mService.getGlobalConfiguration()进行比较,如果不同即发生了变化。这里因为没有设置mRequest.globalConfig,所以它为null。globalConfigWillChange也为false。
mRootWindowContainer.getTopDisplayFocusedRootTask()里面目前也就只有一个Home Task。所以这里就得到它。
resolveToHeavyWeightSwitcherIfNeeded()是在必要的情况下(需要满足配置特色条件),在请求的进程uid和当前的重量级进程不同的情况下,需要跳转到HeavyWeightSwitcherActivity中去操作处理。目前这个暂时忽略。
executeRequest(mRequest)就是去执行启动Activity的操作。这个可以参考 Android Activity的启动器ActivityStarter入口。其中Home ActivityRecord添加到Task中,也是由它实现的。这里,它主要是由addOrReparentStartingActivity方法实现的。
下面globalConfigWillChange为false,所以不用更新配置。
mStartActivity、mLastStartActivityRecord都是在executeRequest(mRequest)中赋值的,只不过赋值的地方不同。mLastStartActivityRecord是在ActivityRecord对象刚创建的时候设置的,而mStartActivity是在启动Activity时设置的,所以它俩一致,代表Activity创建成功,所以newActivityCreated为true。
接下来就是调用ActivityMetricsLogger对象的notifyActivityLaunched方法,通知Activity已经启动完。
如果设置了mRequest.waitResult,则需要对它进行处理。并将它的处理结果返回给res。
getExternalResult(res)是在res为START_ABORTED时,也返回START_SUCCESS。
onExecutionComplete()主要是处理启动器类ActivityStarter对象的回收。