WMS层级&移栈

573 阅读7分钟

基于Android R版本分析

WMS层级结构树

WMS层级结构树.png

  • 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;

多屏Display.png

我们知道,在移栈过程中,会涉及到多个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

DisplayContent_create.png

move Stack

moveStackToDisplay.png

其中,最核心的逻辑就是ActivityStack的reparent()方法,该方法用于更新stack对应的parent;

reparent.png

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 Typevaluedesc
ACTIVITY_TYPE_UNDEFINED0undefined
ACTIVITY_TYPE_STANDARD1标准Activity Type
ACTIVITY_TYPE_HOME2Home/Launcher Activity Type
ACTIVITY_TYPE_RECENTS3Recents/Overview Activity Type 系统中只有一个Activity具有这种类型
ACTIVITY_TYPE_ASSISTANT4辅助 Activity Type
ACTIVITY_TYPE_DREAM5休眠 Activity Type
WindowingMode
WindowingModevaluedesc
WINDOWING_MODE_UNDEFINED0undefined
WINDOWING_MODE_FULLSCREEN1全屏
WINDOWING_MODE_PINNED2画中画
WINDOWING_MODE_SPLIT_SCREEN_PRIMARY3分屏 - 主屏
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY4分屏 - 副屏
WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY = WINDOWING_MODE_SPLIT_SCREEN_SECONDARY4
WINDOWING_MODE_FREEFORM5自由模式
WINDOWING_MODE_MULTI_WINDOW6多窗口

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层,每层可以放置多个窗口,上层窗口覆盖下层;

WM整体图层结构.png

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

move_stack.png