欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

Android 12的新特性解析:深入探讨DisplayArea层级架构(第三部分) - 内容概要与总结

最编程 2024-07-23 17:12:09
...

4 总结

4.1 DisplayArea类型

4.1.1 根据类的继承关系分类

一种是从DisplayArea类的继承关系出发,有以下关系:

class.png

4.1.2 根据DisplayArea.Type类的定义分类

根据DisplayArea.Type类的定义:

    enum Type {
        /** Can only contain WindowTokens above the APPLICATION_LAYER. */
        ABOVE_TASKS,
        /** Can only contain WindowTokens below the APPLICATION_LAYER. */
        BELOW_TASKS,
        /** Can contain anything. */
        ANY;

        // ......
    }

可以将DisplayArea分为三类,分类的依据当前DisplayArea和APPLICATION_LAYER的关系:

            if (mMinLayer > APPLICATION_LAYER) {
                type = DisplayArea.Type.ABOVE_TASKS;
            } else if (mMaxLayer < APPLICATION_LAYER) {
                type = DisplayArea.Type.BELOW_TASKS;
            } else {
                type = DisplayArea.Type.ANY;
            }

这种分类,首先把窗口划分为了App窗口和非App窗口。

其次,对于非App窗口,再根据其层级与App窗口层级的高低关系,分为位于App窗口之上的非App窗口(即ABOVE_TASKS),和位于App窗口之下的非App窗口(即BELOW_TASKS)。

4.1.3 根据Feature分类

在上面分析DisplayArea层级结构的创建流程中,我们在DisplayAreaPolicy.DefaultProvider.configureTrustedHierarchyBuilder方法中看到了6种Feature的添加:

        private void configureTrustedHierarchyBuilder(HierarchyBuilder rootHierarchy,
                WindowManagerService wmService, DisplayContent content) {
            // WindowedMagnification should be on the top so that there is only one surface
            // to be magnified.
            rootHierarchy.addFeature(new Feature.Builder(wmService.mPolicy, "WindowedMagnification",
                    FEATURE_WINDOWED_MAGNIFICATION)
                    .upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
                    .except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
                    // Make the DA dimmable so that the magnify window also mirrors the dim layer.
                    .setNewDisplayAreaSupplier(DisplayArea.Dimmable::new)
                    .build());
            if (content.isDefaultDisplay) {
                // Only default display can have cutout.
                // See LocalDisplayAdapter.LocalDisplayDevice#getDisplayDeviceInfoLocked.
                rootHierarchy.addFeature(new Feature.Builder(wmService.mPolicy, "HideDisplayCutout",
                        FEATURE_HIDE_DISPLAY_CUTOUT)
                        .all()
                        .except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL, TYPE_STATUS_BAR,
                                TYPE_NOTIFICATION_SHADE)
                        .build())
                        .addFeature(new Feature.Builder(wmService.mPolicy,
                                "OneHandedBackgroundPanel",
                                FEATURE_ONE_HANDED_BACKGROUND_PANEL)
                                .upTo(TYPE_WALLPAPER)
                                .build())
                        .addFeature(new Feature.Builder(wmService.mPolicy, "OneHanded",
                                FEATURE_ONE_HANDED)
                                .all()
                                .except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL)
                                .build());
            }
            rootHierarchy
                    .addFeature(new Feature.Builder(wmService.mPolicy, "FullscreenMagnification",
                            FEATURE_FULLSCREEN_MAGNIFICATION)
                            .all()
                            .except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, TYPE_INPUT_METHOD,
                                    TYPE_INPUT_METHOD_DIALOG, TYPE_MAGNIFICATION_OVERLAY,
                                    TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL)
                            .build())
                    .addFeature(new Feature.Builder(wmService.mPolicy, "ImePlaceholder",
                            FEATURE_IME_PLACEHOLDER)
                            .and(TYPE_INPUT_METHOD, TYPE_INPUT_METHOD_DIALOG)
                            .build());
        }
    }

这些Feature都对应一个特定的的DisplayArea。

但是再看DisplayArea的层级结构图:

5.png

还有一些节点的对应Feature却没有看到,即根节点DisplayContent,叶节点TaskDisplayArea和叶节点Leaf对应的DisplayArea.Tokens。

但是每次创建DisplayArea的时候都会传入一个对应的FeatureId的,之前分析的时候可能没有注意:

1)、DisplayContent:

    DisplayContent(Display display, RootWindowContainer root) {
        super(root.mWindowManager, "DisplayContent", FEATURE_ROOT);
        // ......
    }

2)、TaskDisplayArea:

            final TaskDisplayArea defaultTaskDisplayArea = new TaskDisplayArea(content, wmService,
                    "DefaultTaskDisplayArea", FEATURE_DEFAULT_TASK_CONTAINER);

3)、DisplayArea.Tokens:

        Tokens(WindowManagerService wms, Type type, String name) {
            super(wms, type, name, FEATURE_WINDOW_TOKENS);
        }

这样,所有的FeatureID,都已经找到了使用的地方了:

   /**
     * The value in display area indicating that no value has been set.
     */
    public static final int FEATURE_UNDEFINED = -1;

    /**
     * The Root display area on a display
     */
    public static final int FEATURE_SYSTEM_FIRST = 0;

    /**
     * The Root display area on a display
     */
    public static final int FEATURE_ROOT = FEATURE_SYSTEM_FIRST;

    /**
     * Display area hosting the default task container.
     */
    public static final int FEATURE_DEFAULT_TASK_CONTAINER = FEATURE_SYSTEM_FIRST + 1;

    /**
     * Display area hosting non-activity window tokens.
     */
    public static final int FEATURE_WINDOW_TOKENS = FEATURE_SYSTEM_FIRST + 2;

    /**
     * Display area for one handed feature
     */
    public static final int FEATURE_ONE_HANDED = FEATURE_SYSTEM_FIRST + 3;

    /**
     * Display area that can be magnified in
     * {@link Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW}. It contains all windows
     * below {@link WindowManager.LayoutParams#TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY}.
     */
    public static final int FEATURE_WINDOWED_MAGNIFICATION = FEATURE_SYSTEM_FIRST + 4;

    /**
     * Display area that can be magnified in
     * {@link Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN}. This is different from
     * {@link #FEATURE_WINDOWED_MAGNIFICATION} that the whole display will be magnified.
     * @hide
     */
    public static final int FEATURE_FULLSCREEN_MAGNIFICATION = FEATURE_SYSTEM_FIRST + 5;

    /**
     * Display area for hiding display cutout feature
     * @hide
     */
    public static final int FEATURE_HIDE_DISPLAY_CUTOUT = FEATURE_SYSTEM_FIRST + 6;

    /**
     * Display area that the IME container can be placed in. Should be enabled on every root
     * hierarchy if IME container may be reparented to that hierarchy when the IME target changed.
     * @hide
     */
    public static final int FEATURE_IME_PLACEHOLDER = FEATURE_SYSTEM_FIRST + 7;

    /**
     * Display area for one handed background layer, which preventing when user
     * turning the Dark theme on, they can not clearly identify the screen has entered
     * one handed mode.
     * @hide
     */
    public static final int FEATURE_ONE_HANDED_BACKGROUND_PANEL = FEATURE_SYSTEM_FIRST + 8;

    /**
     * The last boundary of display area for system features
     */
    public static final int FEATURE_SYSTEM_LAST = 10_000;

看一下这些Feature的含义都是什么:

  • FEATURE_ROOT,一个屏幕上的根DisplayArea,对应DisplayContent。

  • FEATURE_DEFAULT_TASK_CONTAINER,容纳默认Task容器的DisplayArea,对应TaskDisplayArea。

  • FEATURE_WINDOW_TOKENS,容纳非activity窗口的DisplayArea,对应DisplayArea.Tokens。

  • FEATURE_ONE_HANDED,用于单手模式的DisplayArea,对应名为“OneHanded”的DisplayArea。

  • FEATURE_WINDOWED_MAGNIFICATION,在ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW模式下可以对窗口的某些区域进行放大的DisplayArea,对应名为“WindowedMagnification”的DisplayArea。

  • FEATURE_FULLSCREEN_MAGNIFICATION,在ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN模式下可以对整个屏幕进行放大的DisplayArea,对应名为“FullscreenMagnification”的DisplayArea。

  • FEATURE_HIDE_DISPLAY_CUTOUT,隐藏DisplayCutout的DisplayArea,对应名为“HideDisplayCutout”的DisplayArea。

  • FEATURE_IME_PLACEHOLDER,存放输入法窗口的DisplayArea,对应名为“ImePlaceholder”的DisplayArea。

  • FEATURE_ONE_HANDED_BACKGROUND_PANEL,容纳单手背景图层的DisplayArea,避免用户开启暗黑模式后,无法分辨屏幕是否已经进入了单手模式,对应名为“OneHandedBackgroundPanel”的DisplayArea。

4.2 DisplayArea层级结构生成规则

这里想要讨论一下为什么DisplayArea层级结构呈现为这样的形式:

5.png

在2.3节中分析了生成DisplayArea树的流程,但是感觉不够直观,这里借鉴了:

blog.****.net/shensky711/…

的分析方式,用颜色对各个DisplayArea进行标记。

分析前我们需要知道几点前提:

  • 属于同一层级值的窗口,统一由一个Leaf管理,这个Leaf可以是DisplayArea.Tokens,也可以是TaskDisplayArea或者ImeContainer,这里暂且认为一个Leaf代表的就是同一类型的窗口。
  • 为DisplayArea定义的各种Feature,代表了这个DisplayArea属下的窗口所具有的特征。Leaf虽然本身拥有的Feature,如FEATURE_WINDOW_TOKENS,没有对应的一个具体的功能,但是Leaf又是被层级结构中的父节点所管理的,所以它也会拥有父节点DisplayArea对应的Feature代表的特征。比如一个Leaf的父节点是WindowedMagnification,那么这个Leaf管理的所有窗口都具有窗口放大功能。
  • 另外虽然一个DisplayArea只有一个Feature,但是由于DisplayArea的互相嵌套,那么一个Leaf可能会处于多级DisplayArea之下,所以一个Leaf可能具备多个Feature,比如Leaf:33:35,它的父节点从下往上依次是FullscreenMagnification,OneHanded,HideDisplayCutout,那么这个Leaf下的所有窗口,都具备这些Feature带来的特征。

以此为基础,来分析一下DisplayArea层级结构的生成过程。

1)、由于定义的层级值是0~36,所以最初我们可能为每一个层级值都创建了一个Leaf对象。如果想要某一个Leaf拥有某一个Feature代表的特征,那么就为这个Leaf添加对应的父节点,那么最初的设计可能是这样的:

color1.png

因为有37个Leaf,所以总共有37列。这里的每一个有颜色的格子都代表一个DisplayArea对象。空白格子说明该列的Leaf管理的窗口不希望有该Feature代表的功能,因此没有针对该Feature创建一个DisplayArea对象。

所以这里有37棵独立的DisplayArea树,每一个树的根节点都是一个DisplayContent对象,叶节点都是一个Leaf,然后中间节点则是有多有少,这取决于这棵树的leaf希望拥有哪些Feature。

2)、对于每一棵DisplayArea树,都是父节点连接子节点,中间不能有空白节点。但是上面的表格,我们能看到是有格子是空白的。我们这里是希望表格同样能够反映DisplayArea层级结构,所以我们需要去掉空白格子。

举个例子,看一下36列,该列下的Leaf不需要任何额外Feature,因此不需要再为该Leaf创建任何父DisplayArea,直接将该Leaf添加到DisplayContent的子节点数组中,在表格中就是将该Leaf对应的格子上移,直接移动到DisplayContent所代表的格子之下。

这也就意味着每一个有颜色的格子如果上方有空白格子,那么就将其上移,最终得到:

color2.png

3)、现在每一棵树都是父节点连接子节点,且中间没有空白节点了,但是此时并不够成一个层级结构,而仍然是37棵独立的树,需要进一步优化。首先我们看到,每一个屏幕只对应一个DisplayContent对象,那么这37棵树,它们的根节点其实都是同一个DisplayContent。此外,对于表格中左右相邻的DisplayArea,如果它们的父DisplayArea是同类型的(拥有的Feature相同),那么这种情况下,就可以复用父DisplayArea,即没有必要创建多个DisplayArea,而是只创建一个父DisplayArea,然后将这些左右相邻的DisplayArea全部添加到该父DisplayArea的子节点数组之中,也能达到同样的效果,即这些DisplayArea都具有了父DisplayArea的Feature。此时,这些表格中左右相邻的DisplayArea,由同一个父节点管理,因此表格上看它们左右相邻,在实际的层级结构中,它们也是处于同一层级。

那么表格中如果一个相邻格子的颜色相同,就把这两个格子合并,即DisplayArea的复用。最终得到:

color3.png

再对比之前的树状图:

5.png

也是符合的。

4.3 向DisplayArea层级结构添加窗口

根据DisplyArea树状图可知,对于0~36的每一个层级值,在DisplayArea层级结构中都有相应的Leaf对应。因此每次添加新窗口的时候,只需要将该窗口的窗口类型换算为相应的层级值,然后将该新窗口添加到该层级值对应的Leaf下即可。

层级值反映了一个Leaf在DisplayArea层级结构中的层级高低,层级值越大,该Leaf在DisplayArea层级结构中的层级也越高。而Leaf是窗口的容器,Leaf层级值越大,其管理的窗口在Z轴的顺序也就越高。这也说明了窗口类型值越大的窗口,其在Z轴上的顺序不一定越高,因为窗口类型值和层级值并不是一个正相关的关系。