本文基于Android 11。

SystemUI模块中的Divider管理着所有关于分屏的对象:

  • DividerView(分屏分割线,分屏显示界面)
  • SplitScreenTaskOrganizer(分屏Task组织者,分屏逻辑)

这里重点关注分屏逻辑实现SplitScreenTaskOrganizer。

Devider类实现了DisplayController.OnDisplaysChangedListener,系统启动后回调onDisplayAdded():

// Devider.java@Overridepublic 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 callbacksunregisterOrganizer();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.javaprivate final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap<>();@Overridepublic void registerTaskOrganizer(ITaskOrganizer organizer, int windowingMode) {// 添加到变量 mTaskOrganizersForWindowingModeLinkedList<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.javaboolean 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.javaboolean setTaskOrganizer(ITaskOrganizer organizer) {mTaskOrganizer = organizer;}@Overrideboolean isOrganized() {return mTaskOrganizer != null;}

后续task状态发生变化,通过isOrganized()方法判断是否需要回调通知mTaskOrganizer。

// ActivityStack.java@Overridevoid 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.javaprivate 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.javaint 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.javaTask anyTaskForId(int id, @RootWindowContainer.AnyTaskForIdMatchTaskMode int matchMode,@Nullable ActivityOptions aOptions, boolean onTop) {// 具体要显示的taskfinal 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-primaryfinal 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.javaActivityStack createStackUnchecked(int windowingMode, int activityType, int stackId,boolean onTop, ActivityInfo info, Intent intent, boolean createdByOrganizer) {// 找到副屏 taskTask 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;}// 创建具体要显示的taskfinal 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.javaprivate 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();}}

可以看到当主屏被填满时就开始显示分屏界面了。