system_server侧流程
代码路径:frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
public final int startActivityFromRecents(int taskId, Bundle bOptions) {
mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
"startActivityFromRecents()");
//获取
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
//封装options
final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(bOptions);
final long origId = Binder.clearCallingIdentity();
try {
return mTaskSupervisor.startActivityFromRecents(callingPid, callingUid, taskId,
safeOptions);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
这里callingPid和callingUid为桌面pid和uid,后续传递参数继续调用到ActivityTaskSupervisor.startActivityFromRecents。
代码路径:frameworks/base/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
/**
* Start the given task from the recent tasks. Do not hold WM global lock when calling this
* method to avoid potential deadlock or permission deny by UriGrantsManager when resolving
* activity (see {@link ActivityStarter.Request#resolveActivity} and
* {@link com.android.server.am.ContentProviderHelper#checkContentProviderUriPermission}).
*
* @return The result code of starter.
*/
int startActivityFromRecents(int callingPid, int callingUid, int taskId,
SafeActivityOptions options) {
final Task task;
final int taskCallingUid;
final String callingPackage;
final String callingFeatureId;
final Intent intent;
final int userId;
//获取传递过来的options
final ActivityOptions activityOptions = options != null
? options.getOptions(this)
: null;
boolean moveHomeTaskForward = true;
synchronized (mService.mGlobalLock) {
final boolean isCallerRecents = mRecentTasks.isCallerRecents(callingUid);
int activityType = ACTIVITY_TYPE_UNDEFINED;
if (activityOptions != null) {
activityType = activityOptions.getLaunchActivityType();
//这里主要就是判断是否要 冻结当前最近的任务列表顺序,直到用户与当前应用程序进行交互或发生超时。
if (activityOptions.freezeRecentTasksReordering() && (isCallerRecents
|| ActivityTaskManagerService.checkPermission(MANAGE_ACTIVITY_TASKS,
callingPid, callingUid) == PERMISSION_GRANTED)) {
mRecentTasks.setFreezeTaskListReordering();
}
//传递过来options中没有设置过mLaunchRootTask,因此getLaunchRootTask()的值为空
if (activityOptions.getLaunchRootTask() != null) {
// Don't move home activity forward if there is a launch root set.
moveHomeTaskForward = false;
}
}
//判断当前activity类型的合法性
if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) {
throw new IllegalArgumentException("startActivityFromRecents: Task "
+ taskId + " can't be launch in the home/recents root task.");
}
boolean shouldStartActivity = false;
//延迟窗口布局
mService.deferWindowLayout();
try {
//根据taskId获取Task对象
task = mRootWindowContainer.anyTaskForId(taskId,
MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE, activityOptions, ON_TOP);
//task为空时,做窗口绘制处理并抛出异常
if (task == null) {
mWindowManager.executeAppTransition();
throw new IllegalArgumentException(
"startActivityFromRecents: Task " + taskId + " not found.");
}
if (moveHomeTaskForward) {
// We always want to return to the home activity instead of the recents
// activity from whatever is started from the recents activity, so move
// the home root task forward.
// TODO (b/115289124): Multi-display supports for recents.
//moveHomeRootTaskToFront会获取桌面rootTask,然后将其移至最前台
mRootWindowContainer.getDefaultTaskDisplayArea().moveHomeRootTaskToFront(
"startActivityFromRecents");
}
// If the user must confirm credentials (e.g. when first launching a work
// app and the Work Challenge is present) let startActivityInPackage handle
// the intercepting.
//shouldConfirmCredentials,涉及一些安全性场景,不深究
//task.getRootActivity(),判断当前Task下面最顶端的activity是否存在
if (!mService.mAmInternal.shouldConfirmCredentials(task.mUserId)
&& task.getRootActivity() != null) {
final ActivityRecord targetActivity = task.getTopNonFinishingActivity();
......
try {
//这里是把自由窗口的Task移到最顶部前端
mService.moveTaskToFrontLocked(null /* appThread */,
null /* callingPackage */, task.mTaskId, 0, options);
// Apply options to prevent pendingOptions be taken when scheduling
// activity lifecycle transaction to make sure the override pending app
// transition will be applied immediately.
//activityOptions.getAnimationType()没有设置,值为-1,不走该流程
if (activityOptions != null
&& activityOptions.getAnimationType() == ANIM_REMOTE_ANIMATION) {
targetActivity.mPendingRemoteAnimation =
activityOptions.getRemoteAnimationAdapter();
}
targetActivity.applyOptionsAnimation();
//activityOptions.getLaunchCookie()没有设置,值为null,不走该流程
if (activityOptions != null && activityOptions.getLaunchCookie() != null) {
targetActivity.mLaunchCookie = activityOptions.getLaunchCookie();
}
} finally {
mActivityMetricsLogger.notifyActivityLaunched(launchingState,
START_TASK_TO_FRONT, false /* newActivityCreated */,
targetActivity, activityOptions);
}
mService.getActivityStartController().postStartActivityProcessingForLastStarter(
task.getTopNonFinishingActivity(), ActivityManager.START_TASK_TO_FRONT,
task.getRootTask());
// As it doesn't go to ActivityStarter.executeRequest() path, we need to resume
// app switching here also.
mService.resumeAppSwitches();
return ActivityManager.START_TASK_TO_FRONT;
}
// The task is empty or needs to show the confirmation for credential.
shouldStartActivity = true;
} finally {
if (!shouldStartActivity) {
//继续窗口布局,与前面mService.deferWindowLayout()延迟窗口布局相呼应。
mService.continueWindowLayout();
}
}
......
}
// ActivityStarter will acquire the lock where the places need, so execute the request
// outside of the lock.
try {
// We need to temporarily disable the explicit intent filter matching enforcement
// because Task does not store the resolved type of the intent data, causing filter
// mismatch in certain cases. (b/240373119)
// 从代码流程上看前面条件 (!mService.mAmInternal.shouldConfirmCredentials(task.mUserId) && task.getRootActivity() != null) 为false时才走后续流程。
PackageManagerServiceUtils.DISABLE_ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS.set(true);
//通过startActivityInPackage启动相应的Task中对应Activity,传递options、task等关键参数。
return mService.getActivityStartController().startActivityInPackage(taskCallingUid,
callingPid, callingUid, callingPackage, callingFeatureId, intent, null, null,
null, 0, 0, options, userId, task, "startActivityFromRecents",
false /* validateIncomingUser */, null /* originatingPendingIntent */,
BackgroundStartPrivileges.NONE);
} finally {
PackageManagerServiceUtils.DISABLE_ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS.set(false);
synchronized (mService.mGlobalLock) {
//继续窗口布局,与前面mService.deferWindowLayout()延迟窗口布局相呼应。
mService.continueWindowLayout();
}
}
}
startActivityFromRecents方法主要做了三件事:
- 使用
anyTaskForId方法,通过taskId找到对应的Task并设置其windowingMode。 - 通过
moveHomeRootTaskToFront方法,移动桌面Task至做前台。 - 通过
moveTaskToFrontLocked方法,移动需要切换自由窗口Task移至最前台。
通过taskId找到对应的Task并设置其windowingMode
task = mRootWindowContainer.anyTaskForId(taskId,
MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE, activityOptions, ON_TOP);
这里主要传递应用taskId和activityOptions(携带需要设置的自由窗口相关参数)。
RootWindowContainer.anyTaskForId
代码路径:frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
/**
* Returns a {@link Task} for the input id if available. {@code null} otherwise.
*
* @param id Id of the task we would like returned.
* @param matchMode The mode to match the given task id in.
* @param aOptions The activity options to use for restoration. Can be null.
* @param onTop If the root task for the task should be the topmost on the display.
*/
Task anyTaskForId(int id, @RootWindowContainer.AnyTaskForIdMatchTaskMode int matchMode,
@Nullable ActivityOptions aOptions, boolean onTop) {
// If options are set, ensure that we are attempting to actually restore a task
if (matchMode != MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE && aOptions != null) {
throw new IllegalArgumentException("Should not specify activity options for non-restore"
+ " lookup");
}
//简单说就是创建了一个 task -> task.isTaskId(id) 的lamda表达式
final PooledPredicate p = PooledLambda.obtainPredicate(
Task::isTaskId, PooledLambda.__(Task.class), id);
//查找符合条件的task
Task task = getTask(p);
//将 PooledPredicate 对象回收到对象池中,以便重用,避免频繁的垃圾回收
p.recycle();
if (task != null) {
if (aOptions != null) {
// Resolve the root task the task should be placed in now based on options
// and reparent if needed.
// TODO(b/229927851) For split-screen, setLaunchRootTask is no longer the "root"
// task, consider to rename methods like "parentTask" instead of "rootTask".
//找到rootTask
final Task targetRootTask =
getOrCreateRootTask(null, aOptions, task, onTop);
// When launch with ActivityOptions#getLaunchRootTask, the "root task" just mean the
// parent of current launch, not the "root task" in hierarchy.
//这里是判断当前Task的获取到的rootTask与其task.getRootTask()的场景
//我们一般启动应用时情况如下:
//targetRootTask != null,true
//task.getRootTask() != targetRootTask,false
//task.getParent() != targetRootTask,true
//因此我们这里为false
//这个场景一般是task参数在特殊情况变null时,getOrCreateRootTask中会重新创建Task
if (targetRootTask != null && task.getRootTask() != targetRootTask
&& task.getParent() != targetRootTask) {
final int reparentMode = onTop
? REPARENT_MOVE_ROOT_TASK_TO_FRONT : REPARENT_LEAVE_ROOT_TASK_IN_PLACE;
task.reparent(targetRootTask, onTop, reparentMode, ANIMATE, DEFER_RESUME,
"anyTaskForId");
}
}
//返回获取到的task
return task;
}
//task为空的其他情况,省略
......
if (DEBUG_RECENTS) Slog.w(TAG_RECENTS, "Restored task id=" + id + " from in recents");
return task;
}
这个方法实际上就是通过getOrCreateRootTask做一些操作,主要是给传递的task设置options,并且找到task的rootTask,然后返回给targetRootTask,做后续的条件判断处理,这里最后return的通过getTask(p)获取到的task。
在分析getOrCreateRootTask方法之前,我们先了解下这个条件判断
if (targetRootTask != null && task.getRootTask() != targetRootTask
&& task.getParent() != targetRootTask) {
这里面有几个不同的task,我们看看这个task分别代表什么
targetRootTask:判断通过getOrCreateRootTask(null, aOptions, task, onTop);方法获取到的task的rootTask不为null。
task.getRootTask():获取task的rootTask。
task.getParent():获取task的父节点。
一般情况targetRootTask和task.getRootTask()均为获取当前task的rootTask,因此它们相同,即task.getRootTask() != targetRootTask不成立,所以不会进入到后续条件。
我们结合实际的例子来看便于理解,如下图:
这里我们以需要分屏的应用
com.android.messaging为例,其Task=1265,从图中我们可以看出来该应用只有一个Task,所以其Task就是rootTask,targetRootTask和task.getRootTask()也是Task=1265,其task.getParent()就是DefaultTaskDisplayArea。
再以com.android.launcher3为例,其Task=1261,从图中我们可以看到有多个task镶嵌,targetRootTask和task.getRootTask()值为最顶部的Task=1,task.getParent()即是Task=1261的父节点Task=1,因此这种情况三者全部相等。
注:不熟悉窗口层级结构的朋友可以先看 Android T 窗口层级其一 —— 容器类
这里我们重点分析下getOrCreateRootTask方法。
final Task targetRootTask =
getOrCreateRootTask(null, aOptions, task, onTop);
这里传递了从taskId中找到的对应task,以及前面创建options,其中参数null表示ActivityRecord为空。
RootWindowContainer.getOrCreateRootTask
/**
* Returns the right root task to use for launching factoring in all the input parameters.
*
* @param r The activity we are trying to launch. Can be null.
* @param options The activity options used to the launch. Can be null.
* @param candidateTask The possible task the activity might be launched in. Can be null.
* @param sourceTask The task requesting to start activity. Can be null.
* @param launchParams The resolved launch params to use.
* @param launchFlags The launch flags for this launch.
* @param realCallingPid The pid from {@link ActivityStarter#setRealCallingPid}
* @param realCallingUid The uid from {@link ActivityStarter#setRealCallingUid}
* @return The root task to use for the launch.
*/
Task getOrCreateRootTask(@Nullable ActivityRecord r,
@Nullable ActivityOptions options, @Nullable Task candidateTask,
@Nullable Task sourceTask, boolean onTop,
@Nullable LaunchParamsController.LaunchParams launchParams, int launchFlags) {
// First preference goes to the launch root task set in the activity options.
if (options != null) {
//这边传递过来的options没有设置过mLaunchRootTask
//因此options.getLaunchRootTask()为null,因此不进入此流程
final Task candidateRoot = Task.fromWindowContainerToken(options.getLaunchRootTask());
if (candidateRoot != null && canLaunchOnDisplay(r, candidateRoot)) {
return candidateRoot;
}
}
// Next preference goes to the task id set in the activity options.
if (options != null) {
//options.getLaunchRootTask()为null,因此不进入此流程
final int candidateTaskId = options.getLaunchTaskId();
if (candidateTaskId != INVALID_TASK_ID) {
......
}
}
// Next preference goes to the TaskDisplayArea candidate from launchParams
// or activity options.
TaskDisplayArea taskDisplayArea = null;
//launchParams传递过来的值为null
if (launchParams != null && launchParams.mPreferredTaskDisplayArea != null) {
taskDisplayArea = launchParams.mPreferredTaskDisplayArea;
} else if (options != null) {//options不为空,进入此流程给taskDisplayArea赋值
//当前的options中没有设置,因此options.getLaunchTaskDisplayArea()值为空
//即taskDisplayArea获取的值为null。
final WindowContainerToken daToken = options.getLaunchTaskDisplayArea();
taskDisplayArea = daToken != null
? (TaskDisplayArea) WindowContainer.fromBinder(daToken.asBinder()) : null;
if (taskDisplayArea == null) {
//options.getLaunchDisplayId()为null,因此不进入此流程
final int launchDisplayId = options.getLaunchDisplayId();
if (launchDisplayId != INVALID_DISPLAY) {
final DisplayContent displayContent = getDisplayContent(launchDisplayId);
if (displayContent != null) {
taskDisplayArea = displayContent.getDefaultTaskDisplayArea();
}
}
}
}
//获取activityType
//我们这里r即ActivityRecord为空,因此获取candidateTask的type
//即activityType为ACTIVITY_TYPE_STANDARD
final int activityType = resolveActivityType(r, options, candidateTask);
//此时taskDisplayArea仍然为空
if (taskDisplayArea != null) {
if (canLaunchOnDisplay(r, taskDisplayArea.getDisplayId())) {
return taskDisplayArea.getOrCreateRootTask(r, options, candidateTask,
sourceTask, launchParams, launchFlags, activityType, onTop);
} else {
taskDisplayArea = null;
}
}
// Give preference to the root task and display of the input task and activity if they
// match the mode we want to launch into.
Task rootTask = null;
//传递过来的candidateTask不为空,因此获取其rootTask
if (candidateTask != null) {
rootTask = candidateTask.getRootTask();
}
//rootTask为空且ActivityRecord不为空的情况,通过ActivityRecord获取rootTask
if (rootTask == null && r != null) {
rootTask = r.getRootTask();
}
//launchParams为null,此时windowingMode为WINDOWING_MODE_UNDEFINED
int windowingMode = launchParams != null ? launchParams.mWindowingMode
: WindowConfiguration.WINDOWING_MODE_UNDEFINED;
if (rootTask != null) {
//获取rootTask对应的taskDisplayArea
taskDisplayArea = rootTask.getDisplayArea();
//此时taskDisplayArea不为null
//canLaunchOnDisplay中传递的r(ActivityRecord)和DisplayContent的DisplayId
//其中ActivityRecord为空,从canLaunchOnDisplay方法流程中可以得知该情况返回为true
if (taskDisplayArea != null
&& canLaunchOnDisplay(r, taskDisplayArea.mDisplayContent.mDisplayId)) {
//此时windowingMode为WINDOWING_MODE_UNDEFINED
if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
//该方法通过options.getLaunchWindowingMode()获取了自由窗口的windowingMode,其值为WINDOWING_MODE_FREEFORM
windowingMode = taskDisplayArea.resolveWindowingMode(r, options, candidateTask);
}
// Always allow organized tasks that created by organizer since the activity type
// of an organized task is decided by the activity type of its top child, which
// could be incompatible with the given windowing mode and activity type.
//rootTask.isCompatible(windowingMode, activityType),判断当前应用的windowingMode和activityType
//和新设置的windowingMode和activityType是否一致,一致则返回true,不一致则返回false
//简单来说这个方法就是判断windowingMode和activityType是否需要更新,如果需要更新就继续后面的流程。
//rootTask.mCreatedByOrganizer从代码上看涉及TaskFragment才会有true的情况
if (rootTask.isCompatible(windowingMode, activityType)
|| rootTask.mCreatedByOrganizer) {
//直接返回当前rootTask
return rootTask;
}
} else {
taskDisplayArea = null;
}
}
// Falling back to default task container
//taskDisplayArea为空时,直接获取默认的taskDisplayArea
if (taskDisplayArea == null) {
taskDisplayArea = getDefaultTaskDisplayArea();
}
//在taskDisplayArea中调用getOrCreateRootTask
return taskDisplayArea.getOrCreateRootTask(r, options, candidateTask, sourceTask,
launchParams, launchFlags, activityType, onTop);
}
本次启动自由窗口时,rootTask = candidateTask.getRootTask();不为null,因此进入到rootTask != null的流程。
这里com.android.messaging进入自由窗口为例,使用dump命令adb shell dumpsys activity containers。
从dump中可以看到com.android.messaging的task为1226,在其之上没有别的task,因此rootTask实际上就是candidateTask它本身。
在rootTask.isCompatible(windowingMode, activityType) || rootTask.mCreatedByOrganizer条件中,返回的是false,因此不会直接返回rootTask。
这里我们主要先讲解下rootTask.isCompatible(windowingMode, activityType)
代码路径:frameworks/base/services/core/java/com/android/server/wm/Task.java
public boolean isCompatible(int windowingMode, int activityType) {
// TODO: Should we just move this to ConfigurationContainer?
if (activityType == ACTIVITY_TYPE_UNDEFINED) {
// Undefined activity types end up in a standard root task once the root task is
// created on a display, so they should be considered compatible.
activityType = ACTIVITY_TYPE_STANDARD;
}
return super.isCompatible(windowingMode, activityType);
}
判断activityType类型是否是ACTIVITY_TYPE_UNDEFINED,如果是则改为ACTIVITY_TYPE_STANDARD,之后调用其父类ConfigurationContainer.isCompatible方法
代码路径:frameworks/base/services/core/java/com/android/server/wm/ConfigurationContainer.java
/**
* Returns true if this container is compatible with the input windowing mode and activity type.
* The container is compatible:
* - If {@param activityType} and {@param windowingMode} match this container activity type and
* windowing mode.
* - If {@param activityType} is {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} or
* {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} and this containers activity type is also
* standard or undefined and its windowing mode matches {@param windowingMode}.
* - If {@param activityType} isn't {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} or
* {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} or this containers activity type isn't
* also standard or undefined and its activity type matches {@param activityType} regardless of
* if {@param windowingMode} matches the containers windowing mode.
*/
public boolean isCompatible(int windowingMode, int activityType) {
//获取当前activityType和windowingMode
final int thisActivityType = getActivityType();
final int thisWindowingMode = getWindowingMode();
//比较当前activityType和windowingMode和传递过来的activityType和windowingMode
final boolean sameActivityType = thisActivityType == activityType;
final boolean sameWindowingMode = thisWindowingMode == windowingMode;
//activityType和windowingMode均未发生变化
if (sameActivityType && sameWindowingMode) {
return true;
}
//activityType发生了变化
if ((activityType != ACTIVITY_TYPE_UNDEFINED && activityType != ACTIVITY_TYPE_STANDARD)
|| !isActivityTypeStandardOrUndefined()) {
// Only activity type need to match for non-standard activity types that are defined.
return sameActivityType;
}
// Otherwise we are compatible if the windowing mode is the same.
//windowingMode发生了变化
return sameWindowingMode;
}
通过getActivityType()和getWindowingMode()分别获取了当前activityType和windowingMode,然后进行变化,如果两者之一出现变化则返回false。
因此我们这里没有直接返回rootTask,而是调用到了taskDisplayArea.getOrCreateRootTask。
return taskDisplayArea.getOrCreateRootTask(r, options, candidateTask, sourceTask,
launchParams, launchFlags, activityType, onTop);
这里先回顾说明下传递的参数:
r:ActivityRecord类型,其值由RootWindowContainer.anyTaskForId中传递过来,值为null。
options:ActivityOptions类型,其值从最初桌面侧FreeformSystemShortcut.startActivity调用startActivityFromRecents传递过来,包含来设置 窗口模式(mLaunchWindowingMode) 和 窗口大小(mLaunchBounds) 参数。
candidateTask:Task类型,其值由RootWindowContainer.anyTaskForId中传递过来,值为桌面侧传递过来taskId对应的Task。
sourceTask:Task类型,其值由RootWindowContainer.getOrCreateRootTask传递,值为null。
activityType:其值由RootWindowContainer.getOrCreateRootTask获取并传递,值为ACTIVITY_TYPE_STANDARD即1。
launchParams:LaunchParamsController.LaunchParams类型,其值由RootWindowContainer.getOrCreateRootTask传递,值为null。
launchFlags:int类型,其值由RootWindowContainer.getOrCreateRootTask传递,值为0。
onTop:boolean类型,其值由ActivityTaskSupervisor.startActivityFromRecents的anyTaskForId中传递过来,值为
ActivityTaskSupervisor.ON_TOP即true。
TaskDisplayArea.getOrCreateRootTask
代码路径:frameworks/base/services/core/java/com/android/server/wm/TaskDisplayArea.java
/**
* Returns an existing root task compatible with the input params or creates one
* if a compatible root task doesn't exist.
*
* @see #getOrCreateRootTask(int, int, boolean)
*/
Task getOrCreateRootTask(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
@Nullable Task candidateTask, @Nullable Task sourceTask,
@Nullable LaunchParams launchParams, int launchFlags, int activityType, boolean onTop) {
//设置windowingMode初始值为WINDOWING_MODE_UNDEFINED
int windowingMode = WINDOWING_MODE_UNDEFINED;
if (launchParams != null) {
// If launchParams isn't null, windowing mode is already resolved.
windowingMode = launchParams.mWindowingMode;
} else if (options != null) {
// If launchParams is null and options isn't let's use the windowing mode in the
// options.
//这里options不为空,options.getLaunchWindowingMode()值为WINDOWING_MODE_FREEFORM
windowingMode = options.getLaunchWindowingMode();
}
// Validate that our desired windowingMode will work under the current conditions.
// UNDEFINED windowing mode is a valid result and means that the new root task will inherit
// it's display's windowing mode.
//判断windowingMode有效性,并赋值
windowingMode = validateWindowingMode(windowingMode, r, candidateTask);
//继续传递参数
return getOrCreateRootTask(windowingMode, activityType, onTop, candidateTask, sourceTask,
options, launchFlags);
}
这里主要就是通过options.getLaunchWindowingMode()获取了windowingMode (WINDOWING_MODE_FREEFORM),在将其传递到getOrCreateRootTask方法中。
/**
* When two level tasks are required for given windowing mode and activity type, returns an
* existing compatible root task or creates a new one.
* For one level task, the candidate task would be reused to also be the root task or create
* a new root task if no candidate task.
*
* @param windowingMode The windowing mode the root task should be created in.
* @param activityType The activityType the root task should be created in.
* @param onTop If true the root task will be created at the top of the display,
* else at the bottom.
* @param candidateTask The possible task the activity might be launched in. Can be null.
* @param sourceTask The task requesting to start activity. Used to determine which of the
* adjacent roots should be launch root of the new task. Can be null.
* @param options The activity options used to the launch. Can be null.
* @param launchFlags The launch flags for this launch.
* @return The root task to use for the launch.
* @see #getRootTask(int, int)
*/
Task getOrCreateRootTask(int windowingMode, int activityType, boolean onTop,
@Nullable Task candidateTask, @Nullable Task sourceTask,
@Nullable ActivityOptions options, int launchFlags) {
//我们这里windowingMode传递的是WINDOWING_MODE_FREEFORM,
//因此resolvedWindowingMode为WINDOWING_MODE_FREEFORM
final int resolvedWindowingMode =
windowingMode == WINDOWING_MODE_UNDEFINED ? getWindowingMode() : windowingMode;
// Need to pass in a determined windowing mode to see if a new root task should be created,
// so use its parent's windowing mode if it is undefined.
//我们这里resolvedWindowingMode是WINDOWING_MODE_FREEFORM
//activityType是ACTIVITY_TYPE_STANDARD,因此alwaysCreateRootTask为true
//这取反为false,因此不进入该逻辑
if (!alwaysCreateRootTask(resolvedWindowingMode, activityType)) {
Task rootTask = getRootTask(resolvedWindowingMode, activityType);
if (rootTask != null) {
return rootTask;
}
} else if (candidateTask != null) {
final int position = onTop ? POSITION_TOP : POSITION_BOTTOM;
//这里getLaunchRootTask是同options获取,我们这里options没有设置过mLaunchRootTask,因此为null
final Task launchParentTask = getLaunchRootTask(resolvedWindowingMode, activityType,
options, sourceTask, launchFlags, candidateTask);
//launchParentTask为null不进入该流程
if (launchParentTask != null) {
if (candidateTask.getParent() == null) {
launchParentTask.addChild(candidateTask, position);
} else if (candidateTask.getParent() != launchParentTask) {
candidateTask.reparent(launchParentTask, position);
}
//candidateTask.getDisplayArea()和this都是默认的TaskDisplayArea,即DefaultTaskDisplayArea
//两者相同,即candidateTask.getDisplayArea() != this 为false,因此不进入该流程
} else if (candidateTask.getDisplayArea() != this
|| candidateTask.getRootTask().mReparentLeafTaskIfRelaunch) {
if (candidateTask.getParent() == null) {
addChild(candidateTask, position);
} else {
candidateTask.reparent(this, onTop);
}
}
// Update windowing mode if necessary, e.g. launch into a different windowing mode.
//windowingMode传递的是WINDOWING_MODE_FREEFORM
//candidateTask自身就是rootTask
//candidateTask前后windowingMode有发生变化
if (windowingMode != WINDOWING_MODE_UNDEFINED && candidateTask.isRootTask()
&& candidateTask.getWindowingMode() != windowingMode) {
//ShellTransitions调用collect处理
candidateTask.mTransitionController.collect(candidateTask);
//设置task窗口模式
candidateTask.setWindowingMode(windowingMode);
}
//返回candidateTask的rootTask
return candidateTask.getRootTask();
}
return new Task.Builder(mAtmService)
.setWindowingMode(windowingMode)
.setActivityType(activityType)
.setOnTop(onTop)
.setParent(this)
.setSourceTask(sourceTask)
.setActivityOptions(options)
.setLaunchFlags(launchFlags)
.build();
}
这里我们重点看这一段
if (windowingMode != WINDOWING_MODE_UNDEFINED && candidateTask.isRootTask()
&& candidateTask.getWindowingMode() != windowingMode) {
candidateTask.mTransitionController.collect(candidateTask);
candidateTask.setWindowingMode(windowingMode);
}
return candidateTask.getRootTask();
先解析下这个条件
-
windowingMode != WINDOWING_MODE_UNDEFINEDwindowingMode传递的是WINDOWING_MODE_FREEFORM,因此该条件为true。 -
candidateTask.isRootTask()判断candidateTask是否是rootTask,关于这点前面有讲到,当前candidateTask就是rootTask,因此该条件为true。 -
candidateTask.getWindowingMode() != windowingMode)获取当前candidateTask的windowingMode,判断该windowingMode和传递过来的windowingMode是否一致,我们这里还没有启动到自由窗口,因此candidateTask.getWindowingMode()不是WINDOWING_MODE_FREEFORM,所以不一致,即该条件为true。
综上,所有条件都满足,所有进入该条件。
其中candidateTask.mTransitionController.collect(candidateTask);涉及ShellTransitions,这里不涉及,暂不讲解。
最终通过candidateTask.setWindowingMode(windowingMode)方法给candidateTask的windowingMode设置成了WINDOWING_MODE_FREEFORM。
总结
从mRootWindowContainer.anyTaskForId开始到TaskDisplayArea.getOrCreateRootTask结束,简单概括为一句话就是通过taskId获取对应task并设置该task的windowingMode为WINDOWING_MODE_FREEFORM(自由窗口)。
移动桌面task至做前台
mRootWindowContainer.getDefaultTaskDisplayArea().moveHomeRootTaskToFront(
"startActivityFromRecents");
代码路径:frameworks/base/services/core/java/com/android/server/wm/TaskDisplayArea.java
void moveHomeRootTaskToFront(String reason) {
final Task homeRootTask = getOrCreateRootHomeTask();
if (homeRootTask != null) {
homeRootTask.moveToFront(reason);
}
}
这个方法很简单就是获取桌面的rootTask然后通过homeRootTask.moveToFront(reason);,把桌面task至做前台。
关于Task.moveToFront方法在后面移动需要切换自由窗口Task移至最前台流程中说明。
移动需要切换自由窗口Task移至最前台
mService.moveTaskToFrontLocked(null /* appThread */,
null /* callingPackage */, task.mTaskId, 0, options);
调用到ActivityTaskManagerService.moveTaskToFrontLocked,传递对应参数。
ActivityTaskManagerService.moveTaskToFrontLocked
代码路径:frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
void moveTaskToFrontLocked(@Nullable IApplicationThread appThread,
@Nullable String callingPackage, int taskId, int flags, SafeActivityOptions options) {
......
try {
//1.通过anyTaskForId方法获取task
final Task task = mRootWindowContainer.anyTaskForId(taskId);
......
//获取options,options不为null这把传递过来的SafeActivityOptions转换为ActivityOptions
ActivityOptions realOptions = options != null
? options.getOptions(mTaskSupervisor)
: null;
//2.传递task和options,flags是前面传递过来的0
mTaskSupervisor.findTaskToMoveToFront(task, flags, realOptions, "moveTaskToFront",
false /* forceNonResizable */);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
这个方法主要就是做了两件事:
-
再次通过anyTaskForId方法获取task
final Task task = mRootWindowContainer.anyTaskForId(taskId);这里只传递了taskId,先进入到以下方法 代码路径:frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.javaTask anyTaskForId(int id) { return anyTaskForId(id, MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE); } Task anyTaskForId(int id, @RootWindowContainer.AnyTaskForIdMatchTaskMode int matchMode) { return anyTaskForId(id, matchMode, null, !ON_TOP); } Task anyTaskForId(int id, @RootWindowContainer.AnyTaskForIdMatchTaskMode int matchMode, @Nullable ActivityOptions aOptions, boolean onTop) {从代码中我们可以看出,这一次通过anyTaskForId方法获取task传递的
aOptions的值为null,这里就没有对获取的task进行一些相关设置,只是通过taskId获取该task,具体流程见前面的RootWindowContainer.anyTaskForId流程。 -
使用findTaskToMoveToFront方法传递对应参数
mTaskSupervisor.findTaskToMoveToFront(task, flags, realOptions, "moveTaskToFront", false /* forceNonResizable */);这里主要就是传递获取到的task和options,为后续移动task 到前台做准备。
继续看ActivityTaskSupervisor.findTaskToMoveToFront方法。
ActivityTaskSupervisor.findTaskToMoveToFront
代码路径:frameworks/base/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
/** This doesn't just find a task, it also moves the task to front. */
void findTaskToMoveToFront(Task task, int flags, ActivityOptions options, String reason,
boolean forceNonResizeable) {
//获取当前task的rootTask
Task currentRootTask = task.getRootTask();
if (currentRootTask == null) {
Slog.e(TAG, "findTaskToMoveToFront: can't move task="
+ task + " to front. Root task is null");
return;
}
try {
if ((flags & ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) {
mUserLeaving = true;
}
//延迟窗口布局
mService.deferWindowLayout();
//shell Transition相关
final Transition newTransition = task.mTransitionController.isShellTransitionsEnabled()
? task.mTransitionController.isCollecting() ? null
: task.mTransitionController.createTransition(TRANSIT_TO_FRONT) : null;
task.mTransitionController.collect(task);
reason = reason + " findTaskToMoveToFront";
boolean reparented = false;
//task.isResizeable(),判断task的大小是否可变化
//canUseActivityOptionsLaunchBounds(options),判断当前options是否为null,并且判断系统是否支持画中画或者自由窗口
if (task.isResizeable() && canUseActivityOptionsLaunchBounds(options)) {
//再次获取RootTask
Task targetRootTask =
mRootWindowContainer.getOrCreateRootTask(null, options, task, ON_TOP);
//我们这边targetRootTask和currentRootTask是一样的,不进入此流程
if (targetRootTask != currentRootTask) {
moveHomeRootTaskToFrontIfNeeded(flags, targetRootTask.getDisplayArea(), reason);
task.reparent(targetRootTask, ON_TOP, REPARENT_KEEP_ROOT_TASK_AT_FRONT,
!ANIMATE, DEFER_RESUME, reason);
currentRootTask = targetRootTask;
reparented = true;
// task.reparent() should already placed the task on top,
// still need moveTaskToFrontLocked() below for any transition settings.
}
// The resizeTask must be done after the task is moved to the correct root task,
// because Task's setBounds() also updates dim layer's bounds, but that has
// dependency on the root task.
//从options中获取bounds并设置给task
final Rect bounds = options.getLaunchBounds();
task.setBounds(bounds);
}
//reparented值没有变化,为false,!reparented为true
if (!reparented) {
//这个方法的主要作用是移动桌面rootTask到前台,该流程没有执行,后续分析原因
//这里传递的currentRootTask.getDisplayArea()指的是当前需要切换自由窗口task对应的TaskDisplayArea
moveHomeRootTaskToFrontIfNeeded(flags, currentRootTask.getDisplayArea(), reason);
}
//获取当前task中最顶端的Activity
final ActivityRecord r = task.getTopNonFinishingActivity();
//移动需要切换自由窗口的task至前台
currentRootTask.moveTaskToFront(task, false /* noAnimation */, options,
r == null ? null : r.appTimeTracker, reason);
if (DEBUG_ROOT_TASK) Slog.d(TAG_ROOT_TASK,
"findTaskToMoveToFront: moved to front of root task=" + currentRootTask);
//其他情况以及shell Transition相关
......
} finally {
mUserLeaving = false;
//继续窗口布局,与前面mService.deferWindowLayout()延迟窗口布局相呼应。
mService.continueWindowLayout();
}
}
从代码中我们可以看到,这个方法给我们需要切换到自由窗口的task设置bounds,然后再调用moveTaskToFront移动其到前台。
我们先看几个方法:
-
canUseActivityOptionsLaunchBounds(options)参数options就是前面传递过来包含WindowingMode和Bounds的对象。boolean canUseActivityOptionsLaunchBounds(ActivityOptions options) { // We use the launch bounds in the activity options is the device supports freeform // window management or is launching into the root pinned task. if (options == null || options.getLaunchBounds() == null) { return false; } return (mService.mSupportsPictureInPicture //我们的WindowingMode是WINDOWING_MODE_FREEFORM,不是画中画返回false && options.getLaunchWindowingMode() == WINDOWING_MODE_PINNED) || mService.mSupportsFreeformWindowManagement; }主要看看
mSupportsPictureInPicture和mSupportsFreeformWindowManagement这两个参数是怎么来的。代码路径:frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
public void retrieveSettings(ContentResolver resolver) { final boolean freeformWindowManagement = mContext.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT) || Settings.Global.getInt( resolver, DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0; final boolean supportsMultiWindow = ActivityTaskManager.supportsMultiWindow(mContext); final boolean supportsPictureInPicture = supportsMultiWindow && mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE); ...... synchronized (mGlobalLock) { mForceResizableActivities = forceResizable; ...... final boolean multiWindowFormEnabled = freeformWindowManagement || supportsSplitScreenMultiWindow || supportsPictureInPicture || supportsMultiDisplay; if ((supportsMultiWindow || forceResizable) && multiWindowFormEnabled) { mSupportsMultiWindow = true; mSupportsFreeformWindowManagement = freeformWindowManagement; mSupportsSplitScreenMultiWindow = supportsSplitScreenMultiWindow; mSupportsPictureInPicture = supportsPictureInPicture; mSupportsExpandedPictureInPicture = supportsExpandedPictureInPicture; mSupportsMultiDisplay = supportsMultiDisplay; } else { mSupportsMultiWindow = false; mSupportsFreeformWindowManagement = false; mSupportsSplitScreenMultiWindow = false; mSupportsPictureInPicture = false; mSupportsExpandedPictureInPicture = false; mSupportsMultiDisplay = false; } ......从代码中可以看出
mSupportsPictureInPicture和mSupportsFreeformWindowManagement等参数是用来判断系统是否支持画中画和浮窗等模式。 -
moveHomeRootTaskToFrontIfNeeded(flags, currentRootTask.getDisplayArea(), reason);flags是前面传递过来的0,currentRootTask.getDisplayArea()指的是当前需要切换自由窗口task对应的TaskDisplayArea。private void moveHomeRootTaskToFrontIfNeeded(int flags, TaskDisplayArea taskDisplayArea, String reason) { final Task focusedRootTask = taskDisplayArea.getFocusedRootTask(); if ((taskDisplayArea.getWindowingMode() == WINDOWING_MODE_FULLSCREEN && (flags & ActivityManager.MOVE_TASK_WITH_HOME) != 0) || (focusedRootTask != null && focusedRootTask.isActivityTypeRecents())) { // We move root home task to front when we are on a fullscreen display area and // caller has requested the home activity to move with it. Or the previous root task // is recents. taskDisplayArea.moveHomeRootTaskToFront(reason); } }taskDisplayArea.getFocusedRootTask(),表示获取当前TaskDisplayArea下获取到焦点的RootTask,尚未进入到自由窗口前,当前界面在最近任务,因此当前取到焦点的RootTask就是Launcher(桌面)的RootTask,即focusedRootTask为Launcher的RootTask。taskDisplayArea.getWindowingMode(),表示获取当前TaskDisplayArea的WindowingMode,即WINDOWING_MODE_FULLSCREEN。ActivityManager.MOVE_TASK_WITH_HOME值为1,flags值为0,因此0&1,即值为0。focusedRootTask.isActivityTypeRecents(),判断focusedRootTask的类型是否是ACTIVITY_TYPE_RECENTS,我们这里focusedRootTask为Launcher的RootTask,因此其类型为ACTIVITY_TYPE_HOME。综上,不符合条件,会进入到下面
taskDisplayArea.moveHomeRootTaskToFront(reason)流程。
接下来继续看看切换task至前台的方法。
currentRootTask.moveTaskToFront(task, false /* noAnimation */, options,
r == null ? null : r.appTimeTracker, reason);
这里主要传递需要切换自由窗口的task走后续流程。
Task.moveTaskToFront
代码路径:frameworks/base/services/core/java/com/android/server/wm/Task.java
final void moveTaskToFront(Task tr, boolean noAnimation, ActivityOptions options,
AppTimeTracker timeTracker, String reason) {
moveTaskToFront(tr, noAnimation, options, timeTracker, !DEFER_RESUME, reason);
}
final void moveTaskToFront(Task tr, boolean noAnimation, ActivityOptions options,
AppTimeTracker timeTracker, boolean deferResume, String reason) {
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "moveTaskToFront: " + tr);
......
try {
// Defer updating the IME target since the new IME target will try to get computed
// before updating all closing and opening apps, which can cause the ime target to
// get calculated incorrectly.
mDisplayContent.deferUpdateImeTarget();
// Don't refocus if invisible to current user
//获取当前需要切换自由窗口task顶部非finish的activity
final ActivityRecord top = tr.getTopNonFinishingActivity();
//top为空则通过positionChildAtTop获取顶部task
if (top == null || !top.showToCurrentUser()) {
positionChildAtTop(tr);
if (top != null) {
mTaskSupervisor.mRecentTasks.add(top.getTask());
}
ActivityOptions.abort(options);
return;
}
// Set focus to the top running activity of this task and move all its parents to top.
//设置前面获取的top,把这个activity焦点并把其顶部Task移至最前端
top.moveFocusableActivityToTop(reason);
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to front transition: task=" + tr);
......
//由前面!DEFER_RESUME的值false,传递过来因此,!deferResume为true
if (!deferResume) {
//确保正确的Activity始终处于前台Resumed状态
mRootWindowContainer.resumeFocusedTasksTopActivities();
}
} finally {
mDisplayContent.continueUpdateImeTarget();
}
}
这个方法主要是通过getTopNonFinishingActivity获取当前需要切换自由窗口task顶部非finish的activity,之后使用moveFocusableActivityToTop方法设置该activity焦点并把其顶部Task移至最前端。 我们先看看getTopNonFinishingActivity方法: 代码路径:frameworks/base/services/core/java/com/android/server/wm/TaskFragment.java
ActivityRecord getTopNonFinishingActivity() {
return getTopNonFinishingActivity(true /* includeOverlays */);
}
/**
* Returns the top-most non-finishing activity, even if the activity is NOT ok to show to
* the current user.
* @param includeOverlays whether the task overlay activity should be included.
* @see #topRunningActivity(boolean)
*/
ActivityRecord getTopNonFinishingActivity(boolean includeOverlays) {
// Split into 2 to avoid object creation due to variable capture.
if (includeOverlays) {
return getActivity((r) -> !r.finishing);
}
return getActivity((r) -> !r.finishing && !r.isTaskOverlay());
}
这个方式简单来说就是,从当前task顶部开始遍历,找到第一个finishing属性为false的Activity。 该方法与topRunningActivity()不同,这个方法不检查Activity是否对当前用户可见。
这里参数includeOverlays指的是,是否包含Task Overlay。true:包含Task Overlay的Activity;false:排除Task Overlay的Activity。
Task Overlay是一种特殊的Activity,它覆盖在任务上方,不属于任务正常流程的一部分,不参与任务栈的正常管理,例如:屏幕录制提示,画中画(PIP)控制界面。权限提示对话框,系统级别的覆盖层等。
后面继续分析top.moveFocusableActivityToTop(reason);方法。
这里的top指的是getTopNonFinishingActivity通过获取的需要切换为自由窗口task顶部非finish的activity,所以需要移动至最前端的也是这个activity。
ActivityRecord.moveFocusableActivityToTop
代码路径:frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
/**
* Move activity with its root task to front and make the root task focused.
* @param reason the reason to move to top
* @return {@code true} if the root task is focusable and has been moved to top or the activity
* is not yet resumed while the root task is already on top, {@code false} otherwise.
*/
boolean moveFocusableActivityToTop(String reason) {
if (!isFocusable()) {
ProtoLog.d(WM_DEBUG_FOCUS, "moveFocusableActivityToTop: unfocusable "
+ "activity=%s", this);
return false;
}
//获取的就是前面top的rootTask,即需要切换为自由窗口的根task
final Task rootTask = getRootTask();
if (rootTask == null) {
Slog.w(TAG, "moveFocusableActivityToTop: invalid root task: activity="
+ this + " task=" + task);
return false;
}
// If this activity already positions on the top focused task, moving the task to front
// is not needed. But we still need to ensure this activity is focused because the
// current focused activity could be another activity in the same Task if activities are
// displayed on adjacent TaskFragments.
//这里获取的是当前DisplayContent上焦点的Activity,此时为焦点的mFocusedApp是launcher
final ActivityRecord currentFocusedApp = mDisplayContent.mFocusedApp;
//currentFocusedApp != null,判断当前为焦点的Activity是否为null;
//currentFocusedApp.task == task,判断当前焦点的task和需要切换为自由窗口的task是否相同
//currentFocusedApp.task是launcher和需要切换为自由窗口的task不相同,因此不进入该流程
if (currentFocusedApp != null && currentFocusedApp.task == task) {
......
}
ProtoLog.d(WM_DEBUG_FOCUS, "moveFocusableActivityToTop: activity=%s", this);
//移动需要切换为自由窗口的rootTask至前台
rootTask.moveToFront(reason, task);
// Report top activity change to tracking services and WM
//通知WMactivity层级发生变化
if (mRootWindowContainer.getTopResumedActivity() == this) {
mAtmService.setLastResumedActivityUncheckLocked(this, reason);
}
return true;
}
我们来看这个方法中几个关键参数:
mDisplayContent.mFocusedApp:表示当前DisplayContent上焦点的Activity。以从多任务启动自由窗口为例,尚未进入到自由窗口时,此时的焦点是launcher,因此这里mFocusedApp的值为launcher,代码中赋值给了currentFocusedApp。
currentFocusedApp.task:表示当前焦点Activity的task,如前面所说,这个Activity是launcher,因此这个task也是launcher对应的task。
this:由于该方法是由前面top.moveFocusableActivityToTop(reason)调用到此,top指的是getTopNonFinishingActivity通过获取的需要切换为自由窗口task顶部非finish的activity,因此this,就是这个activity。
task:ActivityRecord成员变量,就是this对应的task。
继续分析移动task到前台的方法。
rootTask.moveToFront(reason, task);
此处任然继续传递需要切换为自由窗口task。
Task.moveToFront
代码路径:frameworks/base/services/core/java/com/android/server/wm/Task.java
/**
* @param reason The reason for moving the root task to the front.
* @param task If non-null, the task will be moved to the top of the root task.
*/
@VisibleForTesting
void moveToFront(String reason, Task task) {
if (!isAttached()) {
return;
}
mTransitionController.recordTaskOrder(this);
final TaskDisplayArea taskDisplayArea = getDisplayArea();
//!isActivityTypeHome(),判断当前task不是桌面类型,因此为true
//returnsToHomeRootTask(),从代码中可以看出我们当前吃的窗口属于多窗口类型,因此为false
//不进入此流程
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) {
task = this;
}
//通过positionChildAt,传递参数POSITION_TOP,把task移动到最前端
task.getParent().positionChildAt(POSITION_TOP, task, true /* includingParents */);
taskDisplayArea.updateLastFocusedRootTask(lastFocusedTask, reason);
}
这个方法中最为关键就是这段代码,也是真正把task移动到最前端的代码。
task.getParent().positionChildAt(POSITION_TOP, task, true /* includingParents */);
先来看看参数:
task:需要切换自由窗口的task。
task.getParent():自由窗口task的父节点,即DefaultTaskDisplayArea。
POSITION_TOP:需要移动task到顶端的参数。
WindowContainer.positionChildAt
我们来看看这个positionChildAt是怎么把task移动到顶部的 代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
/**
* Move a child from it's current place in siblings list to the specified position,
* with an option to move all its parents to top.
* @param position Target position to move the child to.
* @param child Child to move to selected position.
* @param includingParents Flag indicating whether we need to move the entire branch of the
* hierarchy when we're moving a child to {@link #POSITION_TOP} or
* {@link #POSITION_BOTTOM}. When moving to other intermediate positions
* this flag will do nothing.
*/
@CallSuper
void positionChildAt(int position, E child, boolean includingParents) {
if (child.getParent() != this) {
throw new IllegalArgumentException("positionChildAt: container=" + child.getName()
+ " is not a child of container=" + getName()
+ " current parent=" + child.getParent());
}
if (position >= mChildren.size() - 1) {
position = POSITION_TOP;
} else if (position <= 0) {
position = POSITION_BOTTOM;
}
switch (position) {
case POSITION_TOP:
//判断mChildren中最后一项是否是当前child
if (mChildren.peekLast() != child) {
//移除child
mChildren.remove(child);
//添加child
mChildren.add(child);
//通知child位置发生变化
onChildPositionChanged(child);
}
//includingParents,传递过来值为true
//getParent(),当前this是DefaultTaskDisplayArea,其父节点不为空
if (includingParents && getParent() != null) {
//此时DefaultTaskDisplayArea为child继续递归遍历
getParent().positionChildAt(POSITION_TOP, this /* child */,
true /* includingParents */);
}
break;
case POSITION_BOTTOM:
//判断mChildren中第一项是否是当前child
if (mChildren.peekFirst() != child) {
mChildren.remove(child);
mChildren.addFirst(child);
onChildPositionChanged(child);
}
if (includingParents && getParent() != null) {
getParent().positionChildAt(POSITION_BOTTOM, this /* child */,
true /* includingParents */);
}
break;
default:
// TODO: Removing the child before reinserting requires the caller to provide a
// position that takes into account the removed child (if the index of the
// child < position, then the position should be adjusted). We should consider
// doing this adjustment here and remove any adjustments in the callers.
//判断mChildren中child的位置是否在position
if (mChildren.indexOf(child) != position) {
mChildren.remove(child);
mChildren.add(position, child);
onChildPositionChanged(child);
}
}
}
由于我们是通过task.getParent()调用到positionChildAt,因此这里mChildren指的就是task.getParent()之下的所有子节点,即DefaultTaskDisplayArea下的子节点。
实际上这个方法就是把DefaultTaskDisplayArea按照新的顺序重新添加了一遍其子节点。
我们结合dumpsys containers来理解这段代码。
mChildren.peekLast() != child,判断mChildren中最后一项是否是当前child,在最后一项的就是最顶端;
反之mChildren.peekFirst() != child,判断mChildren中第一项是否是当前child,在第一项的就是最底端。
如下图所示:
切换为自由窗口前:
我们可以看到切换自由窗口之前,
Task=1就是最后一项,其编号为#2。
切换为自由窗口后
我们可以看到切换自由窗口之前,
Task=1337就是最后一项,其编号为#2。
总结
从ActivityTaskManagerService.moveTaskToFrontLocked开始,到WindowContainer.positionChildAt结束,简单概括为一句话就是传递需要切换为自由窗口task,确定其顶部activity,将该task移动至最前端显示。
总结
本文主要讲述了从多任务中启动自由窗口的流程,简单可以概括为:
- 桌面端确定需要进入自由窗口的task并传递相应参数。
- 通过SystemUI跨进程调用到system_server侧处理。
- system_server端通过桌面端传递的参数设置其windowingMode,并将该task移动至前台显示。