本文基于Android 11。
SystemUI模块中的Divider管理着所有关于分屏的对象:
- DividerView(分屏分割线,分屏显示界面)
- SplitScreenTaskOrganizer(分屏Task组织者,分屏逻辑)
这里重点关注分屏逻辑实现SplitScreenTaskOrganizer。
Devider类实现了DisplayController.OnDisplaysChangedListener,系统启动后回调onDisplayAdded():
// Devider.java
@Override
public void onDisplayAdded(int displayId) {
mSplits.init();
}
调用了SplitScreenTaskOrganizer对象的init()方法:
class SplitScreenTaskOrganizer extends TaskOrganizer {
void init() throws RemoteException {
registerOrganizer(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
registerOrganizer(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
synchronized (this) {
try {
mPrimary = TaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY,
WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
mSecondary = TaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY,
WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
} catch (Exception e) {
// teardown to prevent callbacks
unregisterOrganizer();
throw e;
}
}
}
}
SplitScreenTaskOrganizer继承了TaskOrganizer,TaskOrganizer给ActivityTaskManager/WindowManager提供了管理task的接口。
/** * Interface for ActivityTaskManager/WindowManager to delegate control of tasks. */
TaskOrganizer又被TaskOrganizerController统一管理。
/** * Stores the TaskOrganizers associated with a given windowing mode and * their associated state. */
回过来看init()方法的实现,主要是两个方法:
- registerOrganizer()
- TaskOrganizer.createRootTask()
1.registerOrganizer
registerOrganizer(int windowingMode)方法接收int类型的windowingMode参数,分别传入了WINDOWING_MODE_SPLIT_SCREEN_PRIMARY和WINDOWING_MODE_SPLIT_SCREEN_SECONDARY参数,registerOrganizer()方法在父类TaskOrganizer实现,通过TaskOrganizerController对象注册。
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY(分屏中的主屏)
- WINDOWING_MODE_SPLIT_SCREEN_SECONDARY(分屏中的副屏)
1.1 TaskOrganizerController
// TaskOrganizerController.java
private final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap<>();
@Override
public void registerTaskOrganizer(ITaskOrganizer organizer, int windowingMode) {
// 添加到变量 mTaskOrganizersForWindowingMode
LinkedList<IBinder> orgs = mTaskOrganizersForWindowingMode.get(windowingMode);
if (orgs == null) {
orgs = new LinkedList<>();
mTaskOrganizersForWindowingMode.put(windowingMode, orgs);
}
orgs.add(organizer.asBinder());
// 添加到 mTaskOrganizerStates 管理。
if (!mTaskOrganizerStates.containsKey(organizer.asBinder())) {
mTaskOrganizerStates.put(organizer.asBinder(),
new TaskOrganizerState(organizer, uid));
}
// 更新task状态,通知task对象已经被organized,关联TaskOrganizer对象,task.setTaskOrganizer(ITaskOrganizer organizer)
mService.mRootWindowContainer.forAllTasks((task) -> {
if (task.getWindowingMode() == windowingMode) {
task.updateTaskOrganizerState(true /* forceUpdate */);
}
});
}
TaskOrganizerController将ITaskOrganizer对象添加到mTaskOrganizerStates管理,mTaskOrganizerStates是一个HashMap,key是Binder对象,value则是内部的代理类TaskOrganizerState,当对应的task状态变化回调时再从这个HashMap取出;更新task状态,通知task对象已经被organized,关联TaskOrganizer对象,task.setTaskOrganizer(ITaskOrganizer organizer)。
1.2 Task
// Task.java
boolean updateTaskOrganizerState(boolean forceUpdate) {
// 从TaskOrganizerController.mTaskOrganizersForWindowingMode获取ITaskOrganizer对象
final ITaskOrganizer org =
mWmService.mAtmService.mTaskOrganizerController.getTaskOrganizer(windowingMode);
final boolean result = setTaskOrganizer(org);
mLastTaskOrganizerWindowingMode = windowingMode;
return result;
}
setTaskOrganizer()方法关联ITaskOrganizer对象,更新变量mLastTaskOrganizerWindowingMode。
// Task.java
boolean setTaskOrganizer(ITaskOrganizer organizer) {
mTaskOrganizer = organizer;
}
@Override
boolean isOrganized() {
return mTaskOrganizer != null;
}
后续task状态发生变化,通过isOrganized()方法判断是否需要回调通知mTaskOrganizer。
// ActivityStack.java
@Override
void onChildPositionChanged(WindowContainer child) {
if (isOrganized()) {
mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(this, false /* force */);
}
}
ActivityStack继承Task,在onChildAdded(),onChildRemoved(),positionChildAt()等改变父子层级的方法都会调用onChildPositionChanged(),如果mTaskOrganizer对象不为null,通过mTaskOrganizerController分发事件信息,接着就是mTaskOrganizerController从mTaskOrganizerStates变量中拿到对象的代理对象TaskOrganizerState回调onTaskAppeared(),onTaskVanished(),onTaskInfoChanged()等。
2.TaskOrganizer.createRootTask
TaskOrganizer.createRootTask()方法很简单,在Display.DEFAULT_DISPLAY中创建两个模式为WINDOWING_MODE_SPLIT_SCREEN_PRIMARY和WINDOWING_MODE_SPLIT_SCREEN_SECONDARY的空Task,不显示任何内容,且和其他task不同的是,其mCreatedByOrganizer为true。
如果是WINDOWING_MODE_SPLIT_SCREEN_PRIMARY模式,TaskDisplayArea还会保存其引用到mRootSplitScreenPrimaryTask变量,以便后续开启分屏时找到对应的task。
// TaskDisplayArea.java
private ActivityStack mRootSplitScreenPrimaryTask;
void addStackReferenceIfNeeded(ActivityStack stack) {
if (windowingMode == WINDOWING_MODE_PINNED) {
mRootPinnedTask = stack;
} else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
mRootSplitScreenPrimaryTask = stack;
}
}
之后要开启分屏模式,就是将要分屏的task分别设置为其子task。
3.开启分屏
ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
options.setSplitScreenCreateMode(SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT);
Bundle optsBundle = options == null ? null : options.toBundle();
ActivityTaskManager.getService().startActivityFromRecents(taskId, optsBundle);
要开启分屏需要在启动时添加ActivityOptions,setLaunchWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY),通过ActivityTaskManagerService.startActivityFromRecents(int taskId, Bundle bOptions)开启。
分屏逻辑大体实现就是找到对应具体要显示的task,如果没有就重新创建,然后找到一开始TaskOrganizer.createRootTask()创建的mPrimary和mSecondary,通过reparent()或者addChild()设置为对应的父子关系。
3.1主屏初始化
// ActivityTaskManagerService.java
int startActivityFromRecents(int callingPid, int callingUid, int taskId,
SafeActivityOptions options) {
final ActivityOptions activityOptions = options != null
? options.getOptions(this)
: null;
// 主屏
task = mRootWindowContainer.anyTaskForId(taskId,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, activityOptions, ON_TOP);
// 副屏
return mService.getActivityStartController().startActivityInPackage(task.mCallingUid,
callingPid, callingUid, callingPackage, callingFeatureId, intent, null, null,
null, 0, 0, options, userId, task, "startActivityFromRecents",
false /* validateIncomingUser */, null /* originatingPendingIntent */,
false /* allowBackgroundActivityStart */);
}
用户开启分屏时,主屏一般是已经存在的task,不需要重新启动,可以通过mRootWindowContainer.anyTaskForId()找到对应的task。
而副屏一般是用户通过点击图标打开的,需要通过mService.getActivityStartController().startActivityInPackage()通过ActivityStater启动。
// RootWindowContainer.java
Task anyTaskForId(int id, @RootWindowContainer.AnyTaskForIdMatchTaskMode int matchMode,
@Nullable ActivityOptions aOptions, boolean onTop) {
// 具体要显示的task
final PooledPredicate p = PooledLambda.obtainPredicate(
Task::isTaskId, PooledLambda.__(Task.class), id);
Task task = getTask(p);
p.recycle();
if (task != null) {
if (aOptions != null) {
// 主屏task: mode=split-screen-primary
final ActivityStack launchStack =
getLaunchStack(null, aOptions, task, onTop);
if (launchStack != null && task.getStack() != launchStack) {
final int reparentMode = onTop
? REPARENT_MOVE_STACK_TO_FRONT : REPARENT_LEAVE_STACK_IN_PLACE;
task.reparent(launchStack, onTop, reparentMode, ANIMATE, DEFER_RESUME,
"anyTaskForId");
}
}
return task;
}
}
先通过taskId找到具体要显示的task,然后通过getLaunchStack()方法找到之前创建的主屏task,第2小节中TaskDisplayArea保存的mRootSplitScreenPrimaryTask引用,将其设置为task的父task,task.reparent()。
3.2 副屏初始化
副屏初始化流程和主屏大同小异,一般需要通过ActivityStarter重新创建新task,设置副屏task为其父task。
// TaskDisplayArea.java
ActivityStack createStackUnchecked(int windowingMode, int activityType, int stackId,
boolean onTop, ActivityInfo info, Intent intent, boolean createdByOrganizer) {
// 找到副屏 task
Task launchRootTask = createdByOrganizer ? null : updateLaunchRootTask(windowingMode);
if (launchRootTask != null) {
// Since this stack will be put into a root task, its windowingMode will be inherited.
windowingMode = WINDOWING_MODE_UNDEFINED;
}
// 创建具体要显示的task
final ActivityStack stack = new ActivityStack(mAtmService, stackId, activityType,
info, intent, createdByOrganizer);
if (launchRootTask != null) {
launchRootTask.addChild(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
if (onTop) {
positionStackAtTop((ActivityStack) launchRootTask, false /* includingParents */);
}
} else {
addChild(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
stack.setWindowingMode(windowingMode, true /* creating */);
}
return stack;
}
对比一下分屏开启前后的window container结构:
- 开启前:
- 开启后:
4.分屏界面显示
在选中主屏task后系统就进入到了分屏界面,前面说过关于主副屏task的状态变化都会回调通知SplitScreenTaskOrganizer,包括上面的设置子task操作(reparent,addChild),回调onTaskInfoChanged(),handleTaskInfoChanged()。
// SplitScreenTaskOrganizer.java
private void handleTaskInfoChanged(RunningTaskInfo info) {
final boolean primaryWasEmpty = mPrimary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
final boolean secondaryWasEmpty = mSecondary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
if (info.token.asBinder() == mPrimary.token.asBinder()) {
mPrimary = info;
} else if (info.token.asBinder() == mSecondary.token.asBinder()) {
mSecondary = info;
}
final boolean primaryIsEmpty = mPrimary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
final boolean secondaryIsEmpty = mSecondary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
if (primaryIsEmpty || secondaryIsEmpty) {
if (mDivider.isDividerVisible()) {
mDivider.startDismissSplit();
} else if (!primaryIsEmpty && primaryWasEmpty && secondaryWasEmpty) {
// 显示分屏界面
mDivider.startEnterSplit();
}
} else if (secondaryImpliesMinimize) {
mDivider.ensureMinimizedSplit();
} else {
mDivider.ensureNormalSplit();
}
}
可以看到当主屏被填满时就开始显示分屏界面了。