基于Android R版本分析
WMS层级结构树
- RootWindowContainer:根窗口容器,树的根是它,通过它遍历寻找,可以找到窗口树上的窗口,它的children是DisplayContent;
- DisplayContent:该类是对应着显示屏幕的,Android是支持多屏幕的,所以可能存在多个DisplayContent对象;
- DisplayArea:该类是对应着显示屏幕下面的,代表一组窗口合集,具有多个子类,如Tokens,TaskDisplayArea等;
- TaskDisplayArea:它为DisplayContent的children,对应着窗口层次为应用层。TaskDisplayArea的children是Task类,其实它的children类型也可以是TaskDisplayArea,而Task的children则可以是ActivityRecord,也可以是Task;
- Tokens:代表专门包含WindowTokens的容器,它的children是WindowToken,而WindowToken的children则为WindowState对象。WindowState是对应着一个窗口的;
- ImeContainer:它是输入法窗口的容器,它的children是WindowToken类型。WindowToken的children为WindowState类型,而WindowState类型则对应着输入法窗口;
- Task:任务,它的children可以是Task,也可以是ActivityRecord类型;
- ActivityRecord:是对应着应用进程中的Activity的,ActivityRecord是继承WindowToken的,它的children类型为WindowState;
- WindowState:WindowState是对应着一个窗口的;
移栈
我们在进行多屏联动的时候移动应用进程,其实移动就是就是对应Activity所在的Stack;
我们知道,在移栈过程中,会涉及到多个DisplayContent之间的操作,而RootWindowContainer作为DisplayContent的Parent,move Stack操作就是在RootWindowContainer中定义:
/**
* Move stack with all its existing content to specified display.
* @param stackId Id of stack to move.
* @param displayId Id of display to move stack to.
* @param onTop Indicates whether container should be place on top or on bottom.
*/
void moveStackToDisplay(int stackId, int displayId, boolean onTop) {
final DisplayContent displayContent = getDisplayContentOrCreate(displayId);
if (displayContent == null) {
throw new IllegalArgumentException("moveStackToDisplay: Unknown displayId="
+ displayId);
}
if (displayContent.isSingleTaskInstance() && displayContent.getStackCount() > 0) {
// We don't allow moving stacks to single instance display that already has a child.
Slog.e(TAG, "Can not move stackId=" + stackId
+ " to single task instance display=" + displayContent);
return;
}
moveStackToTaskDisplayArea(stackId, displayContent.getDefaultTaskDisplayArea(), onTop);
}
- stackId:对应被移动的任务栈;
- displayId:最新承载stack的Display;
- onTop:判断stack在new Display的显示层级,默认是显示在最上层;
DisplayContent create
move Stack
其中,最核心的逻辑就是ActivityStack的reparent()方法,该方法用于更新stack对应的parent;
reparent()方法其中大致执行了三件事:
- oldParent.removeChild:用于将对应的stack从old DisplayContent TaskDisplayArea中移除;
- newParent.addChild:用于更新new DisplayContent中TaskDisplayArea中的stack list,即将对应的stack添加到新的TaskDisplayArea中;
- 调用new DisplayContent的layoutAndAssignWindowLayersIfNeeded更新window窗口内容;
WindowContainer图层结构
我们一般通过 adb shell dumpsys window containers 命令来查看wms图层结构;
/**
* Dumps the names of this container children in the input print writer indenting each
* level with the input prefix.
*/
public void dumpChildrenNames(PrintWriter pw, String prefix) {
final String childPrefix = prefix + " ";
pw.println(getName()
+ " type=" + activityTypeToString(getActivityType()) // Activity Type
+ " mode=" + windowingModeToString(getWindowingMode()) // WindowingMode
+ " override-mode=" + windowingModeToString(getRequestedOverrideWindowingMode()));
// getChildCount对应返回的就是WindowContainer定义的mChildren,该变量类型为WindowList,WindowList继承ArrayList
for (int i = getChildCount() - 1; i >= 0; --i) {
final E cc = getChildAt(i);
pw.print(childPrefix + "#" + i + " ");
cc.dumpChildrenNames(pw, childPrefix);
}
}
Activity Type
| Activity Type | value | desc |
|---|---|---|
| ACTIVITY_TYPE_UNDEFINED | 0 | undefined |
| ACTIVITY_TYPE_STANDARD | 1 | 标准Activity Type |
| ACTIVITY_TYPE_HOME | 2 | Home/Launcher Activity Type |
| ACTIVITY_TYPE_RECENTS | 3 | Recents/Overview Activity Type 系统中只有一个Activity具有这种类型 |
| ACTIVITY_TYPE_ASSISTANT | 4 | 辅助 Activity Type |
| ACTIVITY_TYPE_DREAM | 5 | 休眠 Activity Type |
WindowingMode
| WindowingMode | value | desc |
|---|---|---|
| WINDOWING_MODE_UNDEFINED | 0 | undefined |
| WINDOWING_MODE_FULLSCREEN | 1 | 全屏 |
| WINDOWING_MODE_PINNED | 2 | 画中画 |
| WINDOWING_MODE_SPLIT_SCREEN_PRIMARY | 3 | 分屏 - 主屏 |
| WINDOWING_MODE_SPLIT_SCREEN_SECONDARY | 4 | 分屏 - 副屏 |
| WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY = WINDOWING_MODE_SPLIT_SCREEN_SECONDARY | 4 | |
| WINDOWING_MODE_FREEFORM | 5 | 自由模式 |
| WINDOWING_MODE_MULTI_WINDOW | 6 | 多窗口 |
window containers
// RootWindowContainer
ROOT type=undefined mode=fullscreen override-mode=undefined
#3 Display 0 name="Built-in Screen" type=undefined mode=fullscreen override-mode=fullscreen
#1 mOverlayContainers type=undefined mode=fullscreen override-mode=undefined
#0 mWindowContainers type=undefined mode=fullscreen override-mode=undefined
#0 DisplayArea.Root type=undefined mode=fullscreen override-mode=undefined
#5 Leaf:16:42 type=undefined mode=fullscreen override-mode=undefined
#4 WindowToken{adf04f9 android.os.BinderProxy@62dbd43} type=undefined mode=fullscreen override-mode=undefined
#0 60f2c3e NotificationShade type=undefined mode=fullscreen override-mode=undefined
#3 WindowToken{1cdce4a android.os.BinderProxy@bee87ec} type=undefined mode=fullscreen override-mode=undefined
#0 b791bb StatusBar type=undefined mode=fullscreen override-mode=undefined
#2 WindowToken{b39dca2 android.os.BinderProxy@4e6686d} type=undefined mode=fullscreen override-mode=undefined
#0 287d33 GwmEdgeBackGestureHandler0 type=undefined mode=fullscreen override-mode=undefined
#1 WindowToken{7323ec1 android.os.BinderProxy@9bb1fcb} type=undefined mode=fullscreen override-mode=undefined
#0 9c9b666 NavigationBar0 type=undefined mode=fullscreen override-mode=undefined
#0 WindowToken{749421b android.os.BinderProxy@b90842a} type=undefined mode=fullscreen override-mode=undefined
#0 8378fb8 HVAC Container type=undefined mode=fullscreen override-mode=undefined
#4 ImeContainer type=undefined mode=fullscreen override-mode=undefined
#0 WindowToken{5792cbc android.os.Binder@d32e0af} type=undefined mode=fullscreen override-mode=undefined
#0 63f92a1 InputMethod type=undefined mode=fullscreen override-mode=undefined
#3 Leaf:3:13 type=undefined mode=fullscreen override-mode=undefined
#2 DefaultTaskDisplayArea type=undefined mode=fullscreen override-mode=undefined
#11 Task=144 type=standard mode=fullscreen override-mode=undefined
#0 ActivityRecord{779d144 u0 com.android.permissioncontroller/.permission.ui.ReviewPermissionsActivity t144} type=standard mode=fullscreen override-mode=undefined
#0 80c5d0c com.android.permissioncontroller/com.android.permissioncontroller.permission.ui.ReviewPermissionsActivity type=standard mode=fullscreen override-mode=undefined
#10 Task=143 type=standard mode=fullscreen override-mode=undefined
#0 ActivityRecord{fc3445b u0 com.android.permissioncontroller/.permission.ui.ReviewPermissionsActivity t143} type=standard mode=fullscreen override-mode=undefined
#0 65d23b2 com.android.permissioncontroller/com.android.permissioncontroller.permission.ui.ReviewPermissionsActivity type=standard mode=fullscreen override-mode=undefined
#9 Task=142 type=standard mode=fullscreen override-mode=undefined
#0 ActivityRecord{694bb02 u0 com.android.permissioncontroller/.permission.ui.ReviewPermissionsActivity t142} type=standard mode=fullscreen override-mode=undefined
#0 bcfa8d8 com.android.permissioncontroller/com.android.permissioncontroller.permission.ui.ReviewPermissionsActivity type=standard mode=fullscreen override-mode=undefined
#8 Task=141 type=standard mode=fullscreen override-mode=undefined
#0 ActivityRecord{b7d9a8e u0 com.android.permissioncontroller/.permission.ui.ReviewPermissionsActivity t141} type=standard mode=fullscreen override-mode=undefined
#7 Task=140 type=standard mode=fullscreen override-mode=undefined
#0 ActivityRecord{480d724 u0 com.android.permissioncontroller/.permission.ui.ReviewPermissionsActivity t140} type=standard mode=fullscreen override-mode=undefined
#6 Task=139 type=standard mode=fullscreen override-mode=undefined
#0 ActivityRecord{aa56b30 u0 com.android.permissioncontroller/.permission.ui.ReviewPermissionsActivity t139} type=standard mode=fullscreen override-mode=undefined
#5 Task=138 type=standard mode=fullscreen override-mode=undefined
#0 ActivityRecord{46c6fce u0 com.android.permissioncontroller/.permission.ui.ReviewPermissionsActivity t138} type=standard mode=fullscreen override-mode=undefined
#4 Task=137 type=standard mode=fullscreen override-mode=undefined
#0 ActivityRecord{44e5c72 u0 com.android.permissioncontroller/.permission.ui.ReviewPermissionsActivity t137} type=standard mode=fullscreen override-mode=undefined
#0 3dd7169 com.android.permissioncontroller/com.android.permissioncontroller.permission.ui.ReviewPermissionsActivity type=standard mode=fullscreen override-mode=undefined
#3 Task=136 type=standard mode=fullscreen override-mode=undefined
#0 ActivityRecord{41a2451 u0 com.android.permissioncontroller/.permission.ui.ReviewPermissionsActivity t136} type=standard mode=fullscreen override-mode=undefined
#0 6b1422e com.android.permissioncontroller/com.android.permissioncontroller.permission.ui.ReviewPermissionsActivity type=standard mode=fullscreen override-mode=undefined
#2 Task=135 type=standard mode=fullscreen override-mode=undefined
#0 ActivityRecord{c30782 u0 com.android.permissioncontroller/.permission.ui.ReviewPermissionsActivity t135} type=standard mode=fullscreen override-mode=undefined
#1 Task=134 type=standard mode=fullscreen override-mode=undefined
#0 ActivityRecord{fb0417a u0 com.android.permissioncontroller/.permission.ui.ReviewPermissionsActivity t134} type=standard mode=fullscreen override-mode=undefined
#0 Task=1 type=home mode=fullscreen override-mode=undefined
#0 Task=133 type=home mode=fullscreen override-mode=undefined
#0 ActivityRecord{ed746d4 u0 com.gwm.app.launcher/.Launcher t133} type=home mode=fullscreen override-mode=undefined
#0 3530ae8 com.gwm.app.launcher/com.gwm.app.launcher.Launcher type=home mode=fullscreen override-mode=undefined
#1 Leaf:2:2 type=undefined mode=fullscreen override-mode=undefined
#0 Leaf:0:1 type=undefined mode=fullscreen override-mode=undefined
#0 WallpaperWindowToken{7e79256 token=android.os.Binder@24f5471} type=undefined mode=fullscreen override-mode=fullscreen
#0 f04a14d com.android.systemui.ImageWallpaper type=undefined mode=fullscreen override-mode=undefined
#2 Display 1 name="HDMI Screen" type=undefined mode=fullscreen override-mode=fullscreen
#1 mOverlayContainers type=undefined mode=fullscreen override-mode=undefined
#0 mWindowContainers type=undefined mode=fullscreen override-mode=undefined
#0 DisplayArea.Root type=undefined mode=fullscreen override-mode=undefined
#5 Leaf:16:42 type=undefined mode=fullscreen override-mode=undefined
#4 ImeContainer type=undefined mode=fullscreen override-mode=undefined
#3 Leaf:3:13 type=undefined mode=fullscreen override-mode=undefined
#2 DefaultTaskDisplayArea type=undefined mode=fullscreen override-mode=undefined
#1 Leaf:2:2 type=undefined mode=fullscreen override-mode=undefined
#0 Leaf:0:1 type=undefined mode=fullscreen override-mode=undefined
#1 Display 2 name="Built-in Screen" type=undefined mode=fullscreen override-mode=fullscreen
#1 mOverlayContainers type=undefined mode=fullscreen override-mode=undefined
#0 mWindowContainers type=undefined mode=fullscreen override-mode=undefined
#0 DisplayArea.Root type=undefined mode=fullscreen override-mode=undefined
#5 Leaf:16:42 type=undefined mode=fullscreen override-mode=undefined
#4 ImeContainer type=undefined mode=fullscreen override-mode=undefined
#3 Leaf:3:13 type=undefined mode=fullscreen override-mode=undefined
#2 DefaultTaskDisplayArea type=undefined mode=fullscreen override-mode=undefined
#1 Leaf:2:2 type=undefined mode=fullscreen override-mode=undefined
#0 Leaf:0:1 type=undefined mode=fullscreen override-mode=undefined
#0 Display 3 name="Built-in Screen" type=undefined mode=fullscreen override-mode=fullscreen
#1 mOverlayContainers type=undefined mode=fullscreen override-mode=undefined
#0 mWindowContainers type=undefined mode=fullscreen override-mode=undefined
#0 DisplayArea.Root type=undefined mode=fullscreen override-mode=undefined
#5 Leaf:16:42 type=undefined mode=fullscreen override-mode=undefined
#4 ImeContainer type=undefined mode=fullscreen override-mode=undefined
#3 Leaf:3:13 type=undefined mode=fullscreen override-mode=undefined
#2 DefaultTaskDisplayArea type=undefined mode=fullscreen override-mode=undefined
#1 Leaf:2:2 type=undefined mode=fullscreen override-mode=undefined
#0 Leaf:0:1 type=undefined mode=fullscreen override-mode=undefined
Window{60f2c3e u0 NotificationShade}
Window{b791bb u0 StatusBar}
Window{287d33 u0 GwmEdgeBackGestureHandler0}
Window{9c9b666 u0 NavigationBar0}
Window{8378fb8 u0 HVAC Container}
Window{63f92a1 u0 InputMethod}
Window{80c5d0c u0 com.android.permissioncontroller/com.android.permissioncontroller.permission.ui.ReviewPermissionsActivity}
Window{65d23b2 u0 com.android.permissioncontroller/com.android.permissioncontroller.permission.ui.ReviewPermissionsActivity}
Window{bcfa8d8 u0 com.android.permissioncontroller/com.android.permissioncontroller.permission.ui.ReviewPermissionsActivity}
Window{3dd7169 u0 com.android.permissioncontroller/com.android.permissioncontroller.permission.ui.ReviewPermissionsActivity}
Window{6b1422e u0 com.android.permissioncontroller/com.android.permissioncontroller.permission.ui.ReviewPermissionsActivity}
Window{3530ae8 u0 com.gwm.app.launcher/com.gwm.app.launcher.Launcher}
Window{f04a14d u0 com.android.systemui.ImageWallpaper}
每个显示屏幕的窗口层级分为43层,0-42层,每层可以放置多个窗口,上层窗口覆盖下层;
am display move-stack
am提供了相关的命令可以模拟移栈操作:am display move-stack [taskid] [displayId]
display [COMMAND] [...]: sub-commands for operating on displays.
move-stack <STACK_ID> <DISPLAY_ID>
Move <STACK_ID> from its current display to <DISPLAY_ID>.
adb shell am display move-stack 144 1