Android T 窗口层级其一 —— 容器类 Android安卓

窗口在App端是以PhoneWindow的形式存在,承载了一个Activity的View层级结构。这里我们探讨一下WMS端窗口的形式。 可以通过adb shell dumpsys activity containers 来看窗口显示的层级

窗口容器类 —— WindowContainer类

/**
 * Defines common functionality for classes that can hold windows directly or through their
 * children in a hierarchy form.
 * The test class is {@link WindowContainerTests} which must be kept up-to-date and ran anytime
 * changes are made to this class.
 */
class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
        implements Comparable<WindowContainer>, Animatable, SurfaceFreezer.Freezable,
        InsetsControlTarget {
    ......
            /**
     * The parent of this window container.
     * For removing or setting new parent {@link #setParent} should be used, because it also
     * performs configuration updates based on new parent's settings.
     */
    private WindowContainer<WindowContainer> mParent = null;
    ......
    // List of children for this window container. List is in z-order as the children appear on
    // screen with the top-most window container at the tail of the list.
    protected final WindowList<E> mChildren = new WindowList<E>();

WindowContainer注释中开头就说明了其作用,即给可以直接持有窗口的自己或它的孩子定义了一些公共的方法和属性。 WindowContainer定义了能够直接或者间接以层级结构的形式持有窗口的类的通用功能。 从类的定义和名称,可以看到WindowContainer是一个容器类,可以容纳WindowContainer及其子类对象。如果另外一个容器类作为WindowState的容器,那么这个容器类需要继承WindowContainer或其子类。

其中mParent和mChildren,一个代表父节点一个代表子节点,而且子节点的list顺序代表就是z轴的层级显示顺序,list尾巴在比list的头的z轴层级要高。 1)mParent是WindowContainer类型成员变量,保存的是当前WindowContainer的父容器的引用。 2)mChildren是WindowList类型的成员变量,保存的则是当前WindowContainer持有的所有子容器。并且列表的顺序也就是子容器出现在屏幕上的顺序,最顶层的子容器位于队尾。

根窗口容器 —— RootWindowContainer

/** Root {@link WindowContainer} for the device. */
public class RootWindowContainer extends WindowContainer<DisplayContent>

根窗口容器,树的根是它。通过它遍历寻找,可以找到窗口树上的窗口。它的孩子是DisplayContent。

屏幕 —— DisplayContent

/**
 * Utility class for keeping track of the WindowStates and other pertinent contents of a
 * particular Display.
 */
class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.DisplayContentInfo {

该类是对应着显示屏幕的,Android是支持多屏幕的,所以可能存在多个DisplayContent对象。上图只画了一个对象的结构,其他对象的结构也是和画的对象的结构是相似的。


RootWindowContainer代表dumpsys containers中ROOT ,DisplayContent代表dumpsys containers中Display,0表示当前显示的屏幕


窗口 —— WindowState类

/** A window in the window manager. */
class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState,
        InsetsControlTarget, InputTarget {

在WMS窗口体系中,一个WindowState对象就代表了一个窗口,其继承WindowContainer,这就说明WindowState同样可以作为其他窗口的父容器,例如我们常见的PopupWindow

WindowState的容器

可直接持有WindowState的容器即WindowToken和ActivityRecord,WallpaperWindowToken也可以持有WindowState,而其是继承WindowToken的

WindowToken类

/**
 * Container of a set of related windows in the window manager. Often this is an AppWindowToken,
 * which is the handle for an Activity that it uses to display windows. For nested windows, there is
 * a WindowToken created for the parent window to manage its children.
 */
class WindowToken extends WindowContainer<WindowState> {

这个注释的意思大概是说:窗口管理器中一组相关窗口的容器。这通常是一个AppWindowToken,它是用于显示窗口的“活动”的句柄。对于嵌套窗口,会为父窗口创建一个WindowToken来管理其子窗口。 总而言之就是用WindowToken来管理WindowState

WallpaperWindowToken类

/**
 * A token that represents a set of wallpaper windows.
 */
class WallpaperWindowToken extends WindowToken {

WallpaperWindowToken继承WindowToken,是用来存放和Wallpaper相关的窗口。

ActivityRecord类

/**
 * An entry in the history task, representing an activity.
 */
public final class ActivityRecord extends WindowToken implements WindowManagerService.AppFreezeListener {

ActivityRecord是WindowToken的子类,在WMS中一个ActivityRecord对象就代表一个Activity对象


一般来说,一个窗口的父容器是WindowToken还是ActivityRecord,是否主动使用ViewManager.addView来添加一个窗口 父容器为WindowToken的情况:APP(含系统应用)主动调用添加窗口方法来添加窗口,如StatusBar、浮窗等。即非Activity窗口 父容器为ActivityRecord的情况:系统侧调用添加窗口方法来添加窗口,如在桌面启动一个应用等。即Activity窗口

从层级角度将窗口划分为: App之上的窗口,父容器为WindowToken,如StatusBar和NavigationBar。 App窗口,父容器为ActivityRecord,如Launcher。 App之下的窗口,父容器为WallpaperWindowToken,如ImageWallpaper窗口


WindowToken的容器 —— DisplayArea.Tokens

    /**
     * DisplayArea that contains WindowTokens, and orders them according to their type.
     */
    public static class Tokens extends DisplayArea<WindowToken> {

包含WindowTokens的容器Tokens,并根据其类型对其进行排序。

ActivityRecord的容器 —— Task

/**
 * {@link Task} is a TaskFragment that can contain a group of activities to perform a certain job.
 * Activities of the same task affinities usually group in the same {@link Task}. A {@link Task}
 * can also be an entity that showing in the Recents Screen for a job that user interacted with.
 * A {@link Task} can also contain other {@link Task}s.
 */
class Task extends TaskFragment {

Task继承TaskFragment,它的孩子可以是Task,也可以是ActivityRecord类型。是一个TaskFragment,它可以包含一组执行特定作业的Activity。具有相同任务相似性的Activity通常在同一任务中分组。任务也可以是显示在用户交互的作业的最近屏幕中的实体。任务还可以包含其他任务。 这些Activity可以是来自同一个App,也可以是来自不同的Apps,Activity之间不一定非得相关联。当我们按home键旁边那个方形键(recent-apps)时,屏幕上展示的就是一个个Task。

/**
 * A basic container that can be used to contain activities or other {@link TaskFragment}, which
 * also able to manage the activity lifecycle and updates the visibilities of the activities in it.
 */
class TaskFragment extends WindowContainer<WindowContainer> {

继承WindowContainer,即Task父类的父类是WindowContainer。一个基本容器,可用于包含Activity或其他TaskFragment,它还能够管理Activity生命周期并更新其中活动的可见性。 从层级结构角度来说,与Activity嵌入(Activity Embedding)、平行视界等场景有关。平行视界的分屏模式时,两个Activity将同时显示,而Task本身不提供这个能力,而是由TaskFragment来实现。TaskFragment插入到Task和Activity之间,分割了Task在屏幕上的显示区域,提供给平行视界的两个Activity。

我们在代码中经常会看到调用asTaskFragment()或者调用asTask(),这个是否存在差异呢? 代码位置:frameworks/base/services/core/java/com/android/server/wm/Task.java

    @Override
    Task asTask() {
        // I'm a task!
        return this;
    }

代码位置:frameworks/base/services/core/java/com/android/server/wm/TaskFragment.java

    @Override
    TaskFragment asTaskFragment() {
        return this;
    }

asTaskFragment()和asTask()方法都是在各自对应的类中重写的WindowContainer中方法。 TaskFragment是Task的父类,大部分场景下都是直接创建Task对象而非TaskFragment对象,因此大部分情况下这两个方法返回的都是Task对象。

而在少数特殊场景下是会创建TaskFragment对象的,不会直接创建Task,比如前面说的Activity嵌入(Activity Embedding)、平行视界,在这种场景下对TaskFragment对象调用asTaskFragment返回的就是TaskFragment对象。

Task的容器 —— TaskDisplayArea

/**
 * {@link DisplayArea} that represents a section of a screen that contains app window containers.
 *
 * The children can be either {@link Task} or {@link TaskDisplayArea}.
 */
final class TaskDisplayArea extends DisplayArea<WindowContainer> {

TaskDisplayArea,代表了屏幕上一块专门用来存放App窗口的区域。 它的子容器可能是Task或者是TaskDisplayArea。


DefaultTaskDisplay是TaskDisplayArea的别名,在代码中有体现,后面说明 从这个dump中我们可以看到TaskDisplayArea下面就是Task,Task下面就是ActivityRecord且有多个,而ActivityRecord下面的a9a1d8b和c4b2818是各自的WindowState


DisplayArea类

/**
 * Container for grouping WindowContainer below DisplayContent.
 *
 * DisplayAreas are managed by a {@link DisplayAreaPolicy}, and can override configurations and
 * can be leashed.
 *
 * DisplayAreas can contain nested DisplayAreas.
 *
 * DisplayAreas come in three flavors, to ensure that windows have the right Z-Order:
 * - BELOW_TASKS: Can only contain BELOW_TASK DisplayAreas and WindowTokens that go below tasks.
 * - ABOVE_TASKS: Can only contain ABOVE_TASK DisplayAreas and WindowTokens that go above tasks.
 * - ANY: Can contain any kind of DisplayArea, and any kind of WindowToken or the Task container.
 *
 * @param <T> type of the children of the DisplayArea.
 */
public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {

用于将WindowContainer分组到DisplayContent下方的容器。 DisplayArea由{@link DisplayAreaPolicy}管理,能够复写Configuration和被绑定到leash上。 DisplayArea可以包含嵌套的DisplayArea。 DisplayAreas有三种风格,以确保窗口具有正确的Z顺序:

  • BELOW_TASKS:只能包含位于任务下方的BELLOW_TASK显示区域和WindowToken。
  • ABOVE_TASKS:只能包含位于任务上方的ABOVE_TASK显示区域和WindowToken。
  • ANY:可以包含任何类型的DisplayArea,以及任何类型的WindowToken或Task容器。

@param<T>DisplayArea的子项的类型。

DisplayArea有三个直接子类,TaskDisplayArea,DisplayArea.Tokens和DisplayArea.Tokens

Task的容器 —— TaskDisplayArea

/**
 * {@link DisplayArea} that represents a section of a screen that contains app window containers.
 *
 * The children can be either {@link Task} or {@link TaskDisplayArea}.
 */
final class TaskDisplayArea extends DisplayArea<WindowContainer> {

TaskDisplayArea为DisplayContent的孩子,对应着窗口层次的第2层。第2层作为应用层,看它的定义:int APPLICATION_LAYER = 2,应用层的窗口是处于第2层。 TaskDisplayArea代表了屏幕上的一个包含App类型的WindowContainer的区域。它的子节点可以是Task,或者是TaskDisplayArea。

public DisplayAreaPolicy instantiate(WindowManagerService wmService,
                DisplayContent content, RootDisplayArea root,
                DisplayArea.Tokens imeContainer) {
		Inject.ResultOne<TaskDisplayArea> result = new Inject.ResultOne<>(new TaskDisplayArea(content, wmService,
                    "DefaultTaskDisplayArea", FEATURE_DEFAULT_TASK_CONTAINER));

在DisplayAreaPolicy.java中有创建,并更名为DefaultTaskDisplayArea

WindowTokens的容器 —— DisplayArea.Tokens

    /**
     * DisplayArea that contains WindowTokens, and orders them according to their type.
     */
    public static class Tokens extends DisplayArea<WindowToken> {

Tokens为DisplayArea的内部类,且继承DisplayArea。 即Tokens代表专门包含WindowTokens的容器,它的孩子是WindowToken,而WindowToken的孩子则为WindowState对象。WindowState是对应着一个窗口的。

输入法的容器 —— ImeContainer

    /**
     * Container for IME windows.
     *
     * This has some special behaviors:
     * - layers assignment is ignored except if setNeedsLayer() has been called before (and no
     *   layer has been assigned since), to facilitate assigning the layer from the IME target, or
     *   fall back if there is no target.
     * - the container doesn't always participate in window traversal, according to
     *   {@link #skipImeWindowsDuringTraversal()}
     */
    private static class ImeContainer extends DisplayArea.Tokens {

ImeContainer为DisplayContent.java的内部类,且继承DisplayArea.Tokens,即同样是一个WindowToken的容器,它的孩子是WindowToken类型。WindowToken的孩子为WindowState类型,而WindowState类型则对应着输入法窗口。

模糊效果 —— DisplayArea.Dimmable

    /**
     * DisplayArea that can be dimmed.
     */
    static class Dimmable extends DisplayArea<DisplayArea> {
        private final Dimmer mDimmer = new Dimmer(this);

Dimmable也是DisplayArea的内部类,从名字可以看出,这类的DisplayArea可以添加模糊效果,并且Dimmable也是一个DisplayArea类型的DisplayArea容器。 可以通过Dimmer对象mDimmer施加模糊效果,模糊图层可以插入到以该Dimmable对象为根节点的层级结构之下的任意两个图层之间。 且它有一个直接子类,RootDisplayArea。

DisplayArea层级结构的根节点 —— RootDisplayArea

/**
 * Root of a {@link DisplayArea} hierarchy. It can be either the {@link DisplayContent} as the root
 * of the whole logical display, or a {@link DisplayAreaGroup} as the root of a partition of the
 * logical display.
 */
class RootDisplayArea extends DisplayArea.Dimmable {

{@link DisplayArea}层次结构的根。它可以是作为整个逻辑显示的根的{@link DisplayContent},也可以是作为逻辑显示的分区的根的{@link DisplayAreaGroup}。 即: DisplayContent,作为整个屏幕的DisplayArea层级结构根节点。 DisplayAreaGroup,作为屏幕上部分区域对应的DisplayArea层级结构的根节点

屏幕 —— DisplayContent

/**
 * Utility class for keeping track of the WindowStates and other pertinent contents of a
 * particular Display.
 */
class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.DisplayContentInfo {

用于跟踪特定显示器的WindowStates和其他相关内容的实用程序类,总而言之就是代表一个屏幕。 隶属于同一个DisplayContent的窗口将会被显示在同一个屏幕中。每一个DisplayContent都对应着唯一ID

DisplayAreaGroup
/** The root of a partition of the logical display. */
class DisplayAreaGroup extends RootDisplayArea {

逻辑显示分区的根

容器层级树状图

结合前面各容器和dumpsys activity containers我们得到如下简化的容器层级树状图
如果你看到了这里,觉得文章写得不错就给个赞呗?
更多Android进阶指南 可以扫码 解锁更多Android进阶资料


在这里插入图片描述
敲代码不易,关注一下吧。ღ( ´・ᴗ・` )

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值