背景:
在我们做多窗口开发过程中一般有以下几种模式:
public static final int WINDOWING_MODE_FULLSCREEN = 1;
/** Always on-top (always visible). of other siblings in its parent container. */
public static final int WINDOWING_MODE_PINNED = 2;
/** Can be freely resized within its parent container. */
// TODO: Remove once freeform is migrated to wm-shell.
public static final int WINDOWING_MODE_FREEFORM = 5;
/** Generic multi-window with no presentation attribution from the window manager. */
public static final int WINDOWING_MODE_MULTI_WINDOW = 6;
明显自由窗口和pip模式可以在系统中直接通过windowingMode的值进行直接判断,这时候大家是否想过我们分屏模式应该如何判断啊?
可能很多同学会有如下回答:
直接使用windowingMode判断,看看是否为WINDOWING_MODE_MULTI_WINDOW不就行了。
但是大家注意哈,正常情况下确实一般分屏模式的两个task是mode为multi-window,但是大家注意mutilwindow却不一定为分屏模式哈,比如以前的车载桌面那个Activity嵌入显示的模式,也是multiwindow但不是分屏模式。
那么有啥好办法可以判断当前显示的Activity或者task是否为分屏呢?下面本文就带大家来剖析一下该问题。
系统如何判断分屏模式?
在系统代码中收到如下代码来判断当前显示的Task是否处于分屏模式
final boolean inSplitScreenMode =
defaultTaskDisplayArea.getRootTask(task -> task.isVisible()
&& task.getTopLeafTask().getAdjacentTask() != null)
!= null;
针对上面代码展开源码分析有如下几个过程:
1、遍历defaultTaskDisplayArea这个节点下面的所有RootTask
2、接下来开始挨个RootTask进行,判断条件
a. task是显示状态isVisible为true
b. 判断task的最顶部的叶子task
c. 判断叶子task的AdjacentTask是否不为null
如果不为null说明就是分屏模式。
上面判断代码让大家最疑惑的应该是为啥可以通过getAdjacentTask来判断是否为分屏模式呢?
源码分析adjacent相关设置
上面判断为分屏模式代码中,关键就是靠
这里的AdjacentTask属于熟悉而又陌生的方法,熟悉在于你经常见到它在framework代码中,陌生在于你可能没有本质理解它什么意思,想不到它的实际场景。
Adjacent 翻译后的中文意思就是“ 相邻;邻近的 ”,也就是mAdjacentTaskFragment本质上代表是一个邻居Task。
/** The TaskFragment that is adjacent to this one. */
@Nullable
private TaskFragment mAdjacentTaskFragment;
那么这个邻居Task字面意思理解就是两个Task在一起,相邻着的意思。
那么我们关键就看看mAdjacentTaskFragment到底是在哪里进行的赋值,通过代码调试追查发现如下地方进行的设置。
分屏设置AdjacentTask的调用堆栈:
setAdjacentTaskFragment:447, TaskFragment (com.android.server.wm)
setAdjacentRootsHierarchyOp:2015, WindowOrganizerController (com.android.server.wm)
applyHierarchyOp:1083, WindowOrganizerController (com.android.server.wm)
applyTransaction:718, WindowOrganizerController (com.android.server.wm)
applyTransaction:579, WindowOrganizerController (com.android.server.wm)
applyTransaction:596, WindowOrganizerController (com.android.server.wm)
lambda$applySyncTransaction$0:259, WindowOrganizerController (com.android.server.wm)
accept:0, WindowOrganizerController$$ExternalSyntheticLambda1 (com.android.server.wm)
startLegacySyncOrQueue:1481, TransitionController (com.android.server.wm)
applySyncTransaction:258, WindowOrganizerController (com.android.server.wm)
onTransact:204, WindowOrganizerController (com.android.server.wm)
execTransactInternal:1505, Binder (android.os)
execTransact:1444, Binder (android.os)
明显可以看到是在systemui进程创建分屏的两个task时候会触发这个setAdjacentTaskFragment设置。
这里来详细看看setAdjacentTaskFragment方法:
void setAdjacentTaskFragment(@Nullable TaskFragment taskFragment) {
if (mAdjacentTaskFragment == taskFragment) {
return;
}
resetAdjacentTaskFragment();
if (taskFragment != null) {
mAdjacentTaskFragment = taskFragment;
//这里把当前task设置成参数task的AdjacentTask
taskFragment.setAdjacentTaskFragment(this);
}
}
简单说就是Task1和Task2分别设置对方成为自己的AdjacentTask,形成如下图的依赖模式
systemui如何触发到system端setAdjacentTaskFragment
frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
StageCoordinator的onRootTaskAppeared方法中触发的设置
其实这里的也很,注释明显
// Make the stages adjacent to each other so they occlude what's behind them.
保证上下分屏的task与彼此相邻。
frameworks/base/core/java/android/window/WindowContainerTransaction.java
/**
* Sets to containers adjacent to each other. Containers below two visible adjacent roots will
* be made invisible. This currently only applies to TaskFragment containers created by
* organizer.
* @param root1 the first root.
* @param root2 the second root.
*/
@NonNull
public WindowContainerTransaction setAdjacentRoots(
@NonNull WindowContainerToken root1, @NonNull WindowContainerToken root2) {
mHierarchyOps.add(HierarchyOp.createForAdjacentRoots(
root1.asBinder(),
root2.asBinder()));
return this;
}
这里主要就是createForAdjacentRoots方法,创建对一个HierarchyOp对象,然后跨进程传递system_server进行task的setAdjacentTaskFragment操作
/** Create a hierarchy op for setting adjacent root tasks. */
public static HierarchyOp createForAdjacentRoots(IBinder root1, IBinder root2) {
return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS)
.setContainer(root1)
.setReparentContainer(root2)
.build();
}