安卓12窗口层次: DisplayArea树

DisplayArea树

DisplayArea树示意图
在这里插入图片描述

DisplayArea概念

DisplayArea树的所有节点都继承于DisplayArea,而DisplayArea继承于WindowContainer,因此每一个节点都是一个container。DisplayArea代表了屏幕的显示区域,该显示区域是从Z值来看的,即在不同的layer上创建不同的DisplayArea。如果一个DisplayArea有子元素,那么代表该DisplayArea从Z轴上还是可分的。

DisplayContent代表一块屏幕,DisplayContent继承于DisplayArea,说明其也是一块显示区域,从z值来看,该块屏幕在不同的layer层都可以显示,因此在不同的layer都可以放DisplayArea。它的子元素类型有:TaskDisplayArea、DisplayAreaGroup、Tokens(DisplayArea的内部内)、DisplayArea(用于feature)。DisplayContent的layer总共有36层(窗口类型有36个),其最多有36个DIsplayArea,而层与层之间的z值差距很大,因此每一层上又可以放很多displayArea。

而DisplayArea树构建好了之后往里面添加activity窗口:task、activityRecord和非activity窗口tokens就构成了一颗完整的树。

窗口树种的所有节点都继承于WindowContainer,代表每一个节点都是一个容器,而每一个容器中的元素类型由父类的泛型类型决定,如:RootWindowContainer继承于WindowContainer, 表明RootWindowContainer中的元素类型为DisplayContent。Task继承于WindowContainer,表明Task中的元素类型为WindowContainer,实际上可能是ActivityRecord,也可能是Task(嵌套)。Tokens继承于DisplayArea,表明其子元素类型为WindowToken。

而WindowContainer继承ConfigurationContainer,ConfigurationContainer中提供的是和窗口配置相关的信息(例如窗口尺寸),WindowContainer中提供的是子类作为窗口容器所具备的能力(例如增删,排序)。

为什么引入DisplayArea树?

在这里插入图片描述

增加屏幕放大功能,安卓能否放大分组

再增加一个新feature,,支持是否某窗口动态放大缩小显示范围,我们以Constrained和NonConstrained方式进行区分,那个时候可能组织方式又需要改成如下形式:
在这里插入图片描述

可以想象,未来的未来,再想添加新功能的时候,结构将越来越复杂,维护开发过程也势必变的更加艰难。因此引入DisplayArea,在ActivityRecord、WindowState之上引入DisplayArea树,支持动态调整。

增加的新功能包装成 Feature类,Feature指定对那些layer层进行增加功能,然后在这些Layer层建立对应的DisplayArea,相邻Layer且同属于同一个Displayarea的DisplayArea合并成一个DisplayArea。新的Feature在原来的Feature 下建立DisplayArea。比如Feature1是对layer 2和layer 3的窗口进行旋转,而Feature2 是对layer 3 和layer 4 的窗口进行放大,Feature1和feature2共同作用的结果是layer 2 上的窗口先进行旋转再放大,layer3上的窗口只进行旋转,layer4 的窗口只进行放大。每一个DisplayArea的mFeatureId指定了该DisplayArea的功能。

DisplayAreaGroup

再回到DisplayAreaGroup,什么是DisplayAreaGroup?为什么需要DisplayAreaGroup?从字面上解释而言,DisplayAreaGroup就是一组DisplayArea,用来表示屏幕的一部分内容(这部分由DisplayArea组成)。之所以添加DisplayAreaGroup,主要还是为了功能拓展。例如:(使用Google 2021 Bootcamp示例)
在这里插入图片描述

一个屏幕上想显示多个Display的内容,我们想针对某一Display做旋转要求的时候,在没有DisplayAreaGroup的存在下,很难做到Configuration正确更新,就算能做,我们也要在一致性代码里面加上很多的特殊性判断,这个根本就不符合Google manline的要求。在DisplayAreaGroup的加持下,这个要求变的就十分简单。只要针对DisplayAreaGroup做对应的Configuration更新即可。

即DisplayArea是在Z轴维度进行分割窗口层次,而DisplayAreaGroup是在平面层次分割窗口层次。

DisplayArea树产生简介

在这里插入图片描述

HierachyBuilder是用于产生DisplayArea树的类,传入TaskDisplayArea集合、imeContainer和RootDisplayArea,然后调用builder方法就会产生RootDisplayArea为root的DisplayArea树。android 12 引入了DisplayAreaGroup的概念,因此一般一个屏幕同时存在多棵DisplayArea树,一颗是DisplayContent为root的树,其它几棵是DisplayAreaGroup为root的树。每一颗树都是由HierachyBuilder这个类产生的,通过DisplayAreaPolicyBuilder这个类的build的方法,可以同时调用这些HierachyBuilder的build方法建树,让多棵DisplayArea树的构建变得更为简单。

如图所示,产生DisplayArea树要经过上面的过程,为了更方面的调用,可以将上面的过程包装在一个类中,这个类叫做Provider,设置不同的参数,就有不同的Provider。这些Provider都继承Provider接口,可以将具体的Provider存为配置文件,然后从Provider接口的静态方法fromResources中读取配置文件产生Provider,然后调用Provider的instantiate方法产生DisplayAreaPolicy.Result

public interface Provider {
        /**
         * Instantiates a new {@link DisplayAreaPolicy}. It should set up the {@link DisplayArea}
         * hierarchy.
         *
         * @see DisplayAreaPolicy#DisplayAreaPolicy
         */
        DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content,
                RootDisplayArea root, DisplayArea.Tokens imeContainer);

        /**
         * Instantiates the device-specific {@link Provider}.
         */
        static Provider fromResources(Resources res) {
            String name = res.getString(
                    com.android.internal.R.string.config_deviceSpecificDisplayAreaPolicyProvider);
            if (TextUtils.isEmpty(name)) {
                return new DisplayAreaPolicy.DefaultProvider();
            }
            try {
                return (Provider) Class.forName(name).newInstance();
            } catch (ReflectiveOperationException | ClassCastException e) {
                throw new IllegalStateException("Couldn't instantiate class " + name
                        + " for config_deviceSpecificDisplayAreaPolicyProvider:"
                        + " make sure it has a public zero-argument constructor"
                        + " and implements DisplayAreaPolicy.Provider", e);
            }
        }
    }

fromResources方法没有从资源中读到name指定 文件时会产生默认的Provider:DefaultProvider。DefaultProvider没有设置feature和DisplayAreaGroup集合。

相关数据结构:

DisplayAreaPolicyBuilder

HierarchyBuilder mRootHierarchyBuilder;
private final ArrayList<HierarchyBuilder> mDisplayAreaGroupHierarchyBuilders =
            new ArrayList<>();

DisplayAreaPolicyBuilder中含有DisplayArea树集合的builder,包括根displayArea树的buildermRootHierarchyBuilder,和DisplayAreaGroup树的builder集合mDisplayAreaGroupHierarchyBuilders 。这些树的builder都是HierarchyBuilder 类型。最后树构建成功后会返回一个DisplayAreaPolicy类型,决定了该屏幕的窗口结构。

HierarchyBuilder

       private final RootDisplayArea mRoot;
        private final ArrayList<DisplayAreaPolicyBuilder.Feature> mFeatures = new ArrayList<>();
        private final ArrayList<TaskDisplayArea> mTaskDisplayAreas = new ArrayList<>();
        @Nullable
        private DisplayArea.Tokens mImeContainer;

mRoot指定了该树的根节点,通过传入mFeatures 、mTaskDisplayAreas 和mImeContainer构建了一个含有feature节点、task container节点、ime container节点和tokens节点的DisplayArea树。

DisplayAreaPolicy

是一个抽象类,实现类为DisplayAreaPolicyBuilder.Result.

其主要就是提供了一个RooDisplayArea类型的mRoot成员,该成员就是屏幕的DisplayArea树的根节点。DisplayArea树包括一般DisplayArea树和DisplayAreaGroup的DisplayArea树。mRoot其实就是DisplayContent。

**
 * Policy that manages {@link DisplayArea}.
 */
public abstract class DisplayAreaPolicy {
    protected final WindowManagerService mWmService;

    /**
     * The {@link RootDisplayArea} of the whole logical display. All {@link DisplayArea}s must be
     * (direct or indirect) descendants of this area.
     */
    protected final RootDisplayArea mRoot;

    /**
     * Constructs a new {@link DisplayAreaPolicy}
     *
     * @param wmService the window manager service instance
     * @param root the root display area under which the policy operates
     */
    protected DisplayAreaPolicy(WindowManagerService wmService, RootDisplayArea root) {
        mWmService = wmService;
        mRoot = root;
    }

DisplayContent和DisplayAreaGroup继承于RootDisplayArea,RootDisplayArea是DisplayArea树的根。RootDisplayArea本身继承于DisplayArea。树的产生逻辑是,通过DispolicyAreaBulder产生树,而DispolicyAreaBuilder是通过它里面的HierachyBuilder集合产生树,而HierachyBuilder又是通过传入RootDisplayArea,指定了root,传入mTaskDisplayAreas 、mImeContainer产生对应的节点和tokens节点(tokens节点是默认就会产生的)从而产生一颗DisplayArea树,如果传入的rootDisplayArea是DisplayContent则就是总的树,如果是DisplayAreaGroup则就是DisplayAreaGroup树。







具体分析

一 RootWindowContainer产生过程

SystemServer起来后,启动WMS时,在WindowManagerService的构造函数中创建RootWindowContainer实例,并赋值给WMS.mRoot。

调用链:SystemServer.main() -> run() -> startOtherServices() -> WindowManagerService.main() -> new WindowManagerService()

# WindowManagerService.WindowManagerService()
 
    mRoot = new RootWindowContainer(this);

紧接着在建立AMS与WMS的关联的过程中,将mRoot传递到ATMS中,赋值给ATMS.mRootWindowContainer。

调用链:startOtherServices() -> ActivityManagerService.setWindowManager(wm) -> ActivityTaskManagerService.setWindowManager()

# ActivityTaskManagerService
 
    public void setWindowManager(WindowManagerService wm) {
        synchronized (mGlobalLock) {
            mWindowManager = wm;
            // 赋值给ATMS.mRootWindowContainer
            mRootWindowContainer = wm.mRoot;
            mTempConfig.setToDefaults();
            mTempConfig.setLocales(LocaleList.getDefault());
            mConfigurationSeq = mTempConfig.seq = 1;
            mRootWindowContainer.onConfigurationChanged(mTempConfig);
            mLockTaskController.setWindowManager(wm);
            mStackSupervisor.setWindowManager(wm);
            // 接下来初始化DisplayContent
            mRootWindowContainer.setWindowManager(wm);
        }
    }

2、创建DisplayContent

从DisplayManagerService中获取屏幕集合,然后为每个屏幕创建DisplayContent

void setWindowManager(WindowManagerService wm) {
        mWindowManager = wm;
        mDisplayManager = mService.mContext.getSystemService(DisplayManager.class);
        mDisplayManager.registerDisplayListener(this, mService.mUiHandler);
        mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);

        final Display[] displays = mDisplayManager.getDisplays();
        for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) {
            final Display display = displays[displayNdx];
            final DisplayContent displayContent = new DisplayContent(display, this);
            addChild(displayContent, POSITION_BOTTOM);
            if (displayContent.mDisplayId == DEFAULT_DISPLAY) {
                mDefaultDisplay = displayContent;
            }
        }
        calculateDefaultMinimalSizeOfResizeableTasks();

        final TaskDisplayArea defaultTaskDisplayArea = getDefaultTaskDisplayArea();
        defaultTaskDisplayArea.getOrCreateRootHomeTask(ON_TOP);
        positionChildAt(POSITION_TOP, defaultTaskDisplayArea.mDisplayContent,
                false /* includingParents */);
    }

3 DisplayContent初始化构建窗口层次

DisplayContent(Display display, RootWindowContainer root) {
......
// Setup the policy and build the display area hierarchy.
        mDisplayAreaPolicy = mWmService.getDisplayAreaPolicyProvider().instantiate(
                mWmService, this /* content */, this /* root */, mImeWindowsContainer);
....
}

读取配置com.android.internal.R.string.config_deviceSpecificDisplayAreaPolicyProvider生产mDisplayAreaPolicyProvider 。如果为空则返回 DisplayAreaPolicy.DefaultProvider()

mDisplayAreaPolicyProvider = DisplayAreaPolicy.Provider.fromResources(
                mContext.getResources());
/**
         * Instantiates the device-specific {@link Provider}.
         */
        static Provider fromResources(Resources res) {
            String name = res.getString(
                    com.android.internal.R.string.config_deviceSpecificDisplayAreaPolicyProvider);
            if (TextUtils.isEmpty(name)) {
                return new DisplayAreaPolicy.DefaultProvider();
            }
            try {
                return (Provider) Class.forName(name).newInstance();
            } catch (ReflectiveOperationException | ClassCastException e) {
                throw new IllegalStateException("Couldn't instantiate class " + name
                        + " for config_deviceSpecificDisplayAreaPolicyProvider:"
                        + " make sure it has a public zero-argument constructor"
                        + " and implements DisplayAreaPolicy.Provider", e);
            }
        }

它的Instantiate方法返回DisplayAreaPolicy。现在看看DisplayAreaPolicy.DefaultProvider()的instantiate方法。

@Override
        public DisplayAreaPolicy instantiate(WindowManagerService wmService,
                DisplayContent content, RootDisplayArea root,
                DisplayArea.Tokens imeContainer) {
            final TaskDisplayArea defaultTaskDisplayArea = new TaskDisplayArea(content, wmService,
                    "DefaultTaskDisplayArea", FEATURE_DEFAULT_TASK_CONTAINER);
            final List<TaskDisplayArea> tdaList = new ArrayList<>();
            tdaList.add(defaultTaskDisplayArea);

            // Define the features that will be supported under the root of the whole logical
            // display. The policy will build the DisplayArea hierarchy based on this.
            final HierarchyBuilder rootHierarchy = new HierarchyBuilder(root);
            // Set the essential containers (even if the display doesn't support IME).
            rootHierarchy.setImeContainer(imeContainer).setTaskDisplayAreas(tdaList);
            if (content.isTrusted()) {
                // Only trusted display can have system decorations.
                configureTrustedHierarchyBuilder(rootHierarchy, wmService, content);
            }

            // Instantiate the policy with the hierarchy defined above. This will create and attach
            // all the necessary DisplayAreas to the root.
            return new DisplayAreaPolicyBuilder().setRootHierarchy(rootHierarchy).build(wmService);
        }

创建了一个HierarchyBuilder类型的 rootHierarchy变量。它的mTaskDisplayAreas只含有一个元素,就是defaultTaskDisplayArea ,也设置rootHierarchy的mImeContainer成员。但是DisplayAreaPolicyBuilder没有设置DisplayAreaGroupBuilder。Android S中默认没有开启DisplayAreaGroup和feature。DisplayAreaContent是android S中新引入的一个类。android S中窗口层次大体如下图所示。
在这里插入图片描述

在这里插入图片描述
窗口层次结构,每一个元素的类型都是DisplayArea。具体有TaskDisplayArea,DisplayAreaGroup、DisplayArea.Tokens(非TaskDisplayArea和DisplayAreaGroup节点)和DisplayArea(feature节点)。而在application_layer左边layer的节点叫做BELOW_TASKS, 右边叫做ABOVE_TASKS。而application_layer上的节点叫做any.每一个DisplayAreaGroup单独建立一个层次hierarchy,DisplayAreaPolicyBuilder设置其RootHierarchy和众多DisplayAreaGroupHierarchy,每一个HierarchyBuilder都以其mRoot为根建立hierarchy,有几个HierarchyBuilder就有几棵树。

4 DisplayAreaPolicy 构建过程

Result build(WindowManagerService wmService) {
        validate();

        // Attach DA group roots to screen hierarchy before adding windows to group hierarchies.
        mRootHierarchyBuilder.build(mDisplayAreaGroupHierarchyBuilders);
        List<RootDisplayArea> displayAreaGroupRoots = new ArrayList<>(
                mDisplayAreaGroupHierarchyBuilders.size());
        for (int i = 0; i < mDisplayAreaGroupHierarchyBuilders.size(); i++) {
            HierarchyBuilder hierarchyBuilder = mDisplayAreaGroupHierarchyBuilders.get(i);
            hierarchyBuilder.build();
            displayAreaGroupRoots.add(hierarchyBuilder.mRoot);
        }
        // Use the default function if it is not specified otherwise.
        if (mSelectRootForWindowFunc == null) {
            mSelectRootForWindowFunc = new DefaultSelectRootForWindowFunction(
                    mRootHierarchyBuilder.mRoot, displayAreaGroupRoots);
        }
        return new Result(wmService, mRootHierarchyBuilder.mRoot, displayAreaGroupRoots,
                mSelectRootForWindowFunc);
    }

返回的DisplayAreaBuilder.Result类实现了DisplayAreaPolicy抽象类。

首先调用HierarchyBuilder类mRootHierarchyBuilder的build的方法建立DisplayContent树(根为DisplayContent)然后依次调用mDisplayAreaGroupHierarchyBuilders中每一个HierarchyBuilder类的build方法建立每一个DisplayAreaGroup树(根为DisplayAreaGroup)。

5 DisplayContent树

mRootHierarchyBuilder.build(mDisplayAreaGroupHierarchyBuilders)

/**
         * Builds the {@link DisplayArea} hierarchy below root. And adds the roots of those
         * {@link HierarchyBuilder} as children.
         */
        private void build(@Nullable List<HierarchyBuilder> displayAreaGroupHierarchyBuilders) {
            final WindowManagerPolicy policy = mRoot.mWmService.mPolicy;
            final int maxWindowLayerCount = policy.getMaxWindowLayer() + 1;
            final DisplayArea.Tokens[] displayAreaForLayer =
                    new DisplayArea.Tokens[maxWindowLayerCount];
            final Map<Feature, List<DisplayArea<WindowContainer>>> featureAreas =
                    new ArrayMap<>(mFeatures.size());
// mFeatures用来扩展wm的功能,目前是空的
            for (int i = 0; i < mFeatures.size(); i++) {
                featureAreas.put(mFeatures.get(i), new ArrayList<>());
            }

            // This method constructs the layer hierarchy with the following properties:
            // (1) Every feature maps to a set of DisplayAreas
            // (2) After adding a window, for every feature the window's type belongs to,
            //     it is a descendant of one of the corresponding DisplayAreas of the feature.
            // (3) Z-order is maintained, i.e. if z-range(area) denotes the set of layers of windows
            //     within a DisplayArea:
            //      for every pair of DisplayArea siblings (a,b), where a is below b, it holds that
            //      max(z-range(a)) <= min(z-range(b))
            //
            // The algorithm below iteratively creates such a hierarchy:
            //  - Initially, all windows are attached to the root.
            //  - For each feature we create a set of DisplayAreas, by looping over the layers
            //    - if the feature does apply to the current layer, we need to find a DisplayArea
            //      for it to satisfy (2)
            //      - we can re-use the previous layer's area if:
            //         the current feature also applies to the previous layer, (to satisfy (3))
            //         and the last feature that applied to the previous layer is the same as
            //           the last feature that applied to the current layer (to satisfy (2))
            //      - otherwise we create a new DisplayArea below the last feature that applied
            //        to the current layer

            PendingArea[] areaForLayer = new PendingArea[maxWindowLayerCount];
//**Feature feature, int minLayer, PendingArea parent**
            final PendingArea root = new PendingArea(null, 0, null);
            Arrays.fill(areaForLayer, root);

            // Create DisplayAreas to cover all defined features.
            final int size = mFeatures.size();
            for (int i = 0; i < size; i++) {
                // Traverse the features with the order they are defined, so that the early defined
                // feature will be on the top in the hierarchy.
                final Feature feature = mFeatures.get(i);
                PendingArea featureArea = null;
                for (int layer = 0; layer < maxWindowLayerCount; layer++) {
                    if (feature.mWindowLayers[layer]) {
                        // This feature will be applied to this window layer.
                        //
                        // We need to find a DisplayArea for it:
                        // We can reuse the existing one if it was created for this feature for the
                        // previous layer AND the last feature that applied to the previous layer is
                        // the same as the feature that applied to the current layer (so they are ok
                        // to share the same parent DisplayArea).
                        if (featureArea == null || featureArea.mParent != areaForLayer[layer]) {
                            // No suitable DisplayArea:
                            // Create a new one under the previous area (as parent) for this layer.
                            featureArea = new PendingArea(feature, layer, areaForLayer[layer]);
                            areaForLayer[layer].mChildren.add(featureArea);
                        }
                        areaForLayer[layer] = featureArea;
                    } else {
                        // This feature won't be applied to this window layer. If it needs to be
                        // applied to the next layer, we will need to create a new DisplayArea for
                        // that.
                        featureArea = null;
                    }
                }
            }

            // Create Tokens as leaf for every layer.
            PendingArea leafArea = null;
            int leafType = LEAF_TYPE_TOKENS;
            for (int layer = 0; layer < maxWindowLayerCount; layer++) {
                int type = typeOfLayer(policy, layer);
                // Check whether we can reuse the same Tokens with the previous layer. This happens
                // if the previous layer is the same type as the current layer AND there is no
                // feature that applies to only one of them.
               /* 存在leafArea,则如果leafArea.mParet就是指向areaForLayer[layer],
              且leaftype等于该layer的tye,则复用该leafArea,否则则重新创建leafArea*/
                if (leafArea == null || //上一层有节点
                    leafArea.mParent != areaForLayer[layer] //同属于同一个pendingArea
                        || type != leafType //窗口类型相同。
                       ) {
                    // Create a new Tokens for this layer.
                    leafArea = new PendingArea(null /* feature */, layer, areaForLayer[layer]);
                    areaForLayer[layer].mChildren.add(leafArea);
                    leafType = type;
                     //为layer 2 应用layer
                    if (leafType == LEAF_TYPE_TASK_CONTAINERS) {
                        // We use the passed in TaskDisplayAreas for task container type of layer.
                        // Skip creating Tokens even if there is no TDA.
                        addTaskDisplayAreasToApplicationLayer(areaForLayer[layer]);
                        addDisplayAreaGroupsToApplicationLayer(areaForLayer[layer],
                                displayAreaGroupHierarchyBuilders);
                        leafArea.mSkipTokens = true;
                     //为layer 15 和16 ,输入法layer
                    } else if (leafType == LEAF_TYPE_IME_CONTAINERS) {
                        // We use the passed in ImeContainer for ime container type of layer.
                        // Skip creating Tokens even if there is no ime container.
                        leafArea.mExisting = mImeContainer;
                        leafArea.mSkipTokens = true;
                    }
                }
                leafArea.mMaxLayer = layer;
            }
            root.computeMaxLayer();

            // We built a tree of PendingAreas above with all the necessary info to represent the
            // hierarchy, now create and attach real DisplayAreas to the root.
            root.instantiateChildren(mRoot, displayAreaForLayer, 0, featureAreas);

            // Notify the root that we have finished attaching all the DisplayAreas. Cache all the
            // feature related collections there for fast access.
            mRoot.onHierarchyBuilt(mFeatures, displayAreaForLayer, featureAreas);
        }

PendingArea(Feature feature, int minLayer, PendingArea parent) {
            mMinLayer = minLayer;
            mFeature = feature;
            mParent = parent;

成员

static class PendingArea {
        final int mMinLayer;
        final ArrayList<PendingArea> mChildren = new ArrayList<>();
        final Feature mFeature;
        final PendingArea mParent;
        int mMaxLayer;

        /** If not {@code null}, use this instead of creating a {@link DisplayArea.Tokens}. */
        @Nullable DisplayArea mExisting;

        /**
         * Whether to skip creating a {@link DisplayArea.Tokens} if {@link #mExisting} is
         * {@code null}.
         *
         * <p>This will be set for {@link HierarchyBuilder#LEAF_TYPE_IME_CONTAINERS} and
         * {@link HierarchyBuilder#LEAF_TYPE_TASK_CONTAINERS}, because we don't want to create
         * {@link DisplayArea.Tokens} for them even if they are not set.
         */
        boolean mSkipTokens = false;
}

PendingArea的mSkipTokens 表示这不是一个tokens节点,后面非feature树的时候会说,而mFeature不为null,则表示这是一个feature节点。

HierarchyBuilder的builder方法先构建PendingArea树,最后在将PedingArea树转为DisplayArea树

构建PendingArea树可以分为两个部分,第一个部分根据mFeatures所应用的layer建立feature节点,第二个部分在第一部分构建的PendingArea树基础上再每一个layer都建立tokens节点,构建tokens树,其中如果是应用layer(application_layer,编号为2),则HierarchyBuilder的mTaskDisplayArea和传进来的mDisplayAreaGroupHierarchyBuilders建立PendingArea节点。

5.1 features 树建立

feature实质是对相同性质的layer的分组,结果保存在areaForLayer数组中。数组中的每个元素因其mParent成员而形成树。areForLayer存储的是叶子节点。

对于每一个feature,在该feature支持的每一layer中创建一个PedingArea节点,为了效率,如果前一个相邻layer的PedingArea节点已经创建,且该节点的父亲为areaForLayer[layer],则就不必创建新的PedingArea了,就用前一个PedingArea。

在 Android 12 上,Feature 正式派上用场了,原生添加了以下 Feature:

  • WindowedMagnification: 屏幕放大功能,通過 SystemUI mirrorSurface 该节点实现内容拷贝,详见 WindowMagnificationGestureHandler#toggleMagnification
  • HideDisplayCutout: 隐藏刘海屏功能,开启后,该节点将不会延伸到刘海屏区域(除了状态栏导航栏等窗口,因为不在节点控制范围之内)
  • OneHandedBackgroundPanel: 单手模式下此节点会挂载一个纯色图层(在壁纸图层之下),防止深色模式下分辨不出单手模式
  • OneHanded: 单手模式下相关节点都会做一个向下的位移
    FullscreenMagnification: 屏幕放大功能,通过无障碍服
  • FullScreenMagnificationController.SpecAnimationBridge#setMagnificationSpecLocked 最后调用 DisplayContent#applyMagnificationSpec 方法实现节点放大。不过源码中并不是通过这个 Feature 来实现相关层级放大的,改造得还不彻底
  • ImePlaceholder: 特殊情况下用来放置输入法的节点

建立过程提供两种理解:

1 图形理解

我们知道,Android 系统是有 Z 轴概念的,不同的窗口有不同的高度,所有的窗口类型对应到 WMS 都会有一个 layer 值,layer 越大,显示在越上面,WMS 规定 1~36 层级,每一个 Feature 都指定了它所能影响到的 layer 层。这里用颜色对不同 Feature 能影响 layer 图层进行颜色标记:

在这里插入图片描述

上移之后,我们得到了最终的图表,接下来用以下规则进行层级构建:

同一行相邻的同色块变成一个 Feature 节点,从左到右根据颜色不断生成节点,同一行所有节点挂在同一个父节点下,父节点就是垂直正上方一行的色块对应的节点,为最末端所有 Feature 节点再添加一个节点,根据子节点代表的 layer 不一样,最后添加的节点也不一样除了 layer 是 2、15 和 16 外,挂载 DisplayArea.Tokens(这类节点后续只能添加 WindowToken 节点)layer = 2 (也就是 APPLICATION_LAYER)的节点,挂载 TaskDisplayArealayer ,等于 15 和 16 的节点,挂载 ImeContainer

2 本质理解

第一种理解过于表象,没有将DisplayArea的概念体现出来,本质上feature树的建立过程如下:屏幕的所有layer属于屏幕这个pendingArea所有,假设feature 1作用于1、5 layer,则在屏幕pendingArea的中获得1、5 layer用于建立自己的pendingArea用于显示(窗口显示在上面),则就建立2个pedingArea,且它们的父亲为屏幕pendingArea。feature 2 作用于1 、2、3layer,则其需要从feature 1的pendingArea 1中获取layer1 ,从屏幕pendingArea中获取layer 2 、3。对于layer 1,产生feature2 自己的pendingArea ,其父亲为feature 1 的pendingArea,而对于layer 2、 3由于其相邻且都是从一个pendingArea中分配的,因此只建立一个pedingArea,其mMinLayer为2,mManxLayer 为3,父亲为屏幕pendingLayer。
在这里插入图片描述
通过上述构建规则后,我们可以获得一个树形的层级,并且这棵树有以下特点:

树的最末端节点对应一个 layer 范围,同一个 layer 值只有一个末端节点与之对应为所有 Feature 都生成了对应的父节点,用以控制其所能影响的 layer。生成了这棵树后,我们会保存两样东西:

  • areaForLayer数组,所有 layer 值对应的最末端节点,方便我们后续根据窗口类型添加节点
  • 以 Map<Feature, List> 形式保存的所有 Feature 节点,方便我们后取出某 Feature 对应的所有节点

5.2tokens树和TaskDisplayArea、DisplayAreaGroup节点

和5.1构建的feature树类似,tokens节点作用于所有layer。构建方式和5.1一样,唯一的区别是复用的条件除了和上一layer的pendingArea同属于一个pendingArea外,还需要两个layer的type相同。typeOfLayer指该layer上的叶子节点类型。对于应用层,即APPLICATION_LAYER(layer值为2),类型为task containers,对于layer为等于 15 和 16 的节点,类型为IME_CONTAINERS,其它layer,则类型为tokens。其实ime containers也是一种tokens。

private static int typeOfLayer(WindowManagerPolicy policy, int layer) {
            if (layer == APPLICATION_LAYER) {
                return LEAF_TYPE_TASK_CONTAINERS;
            } else if (layer == policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD)
                    || layer == policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD_DIALOG)) {
                return LEAF_TYPE_IME_CONTAINERS;
            } else {
                return LEAF_TYPE_TOKENS;
            }
        }

在这里插入图片描述

如上图所示的构建过程,layer 1、0同属于feature 2的第一个pedingAarea,且layer 1与2 类型相同,因此合成一个PendingArea,layer 2 、3、4虽然同属于同一个pendingArea,但是layer 2与其它layer 类型不同,因此layer 2单独生成一个pendingArea,3和4生成同一个pendingArea。layer 15 和layer 16虽然类型都是ime container,但是它们分属不同的pendingArea,因此不能合成一个pendingArea,所以生成pendingArea 15和pendingArea16,这两个是特殊的tokens,它们的mSkipTokens为false,mExisting为mImeContainer(mImeContainer是传进来的值)表示后续DisplayArea树的构建过程中,不自动为其生产Tokens节点,而是用mExisting替代(mExisting就是DisplayArea类型)。tokens节点生成之后保存在areaForLayer数组中。

生成了tokens pendingArea树之后,对于layer 2,还要根据mTaskDisplayAreas的值,为其生成pendingArea,其mSkipTokens为false,mExisting为TaskDisplayArea对象。同时如果传进来DisplayAreaGroup集合,还要为其生成对应定pendingArea,其mSkipTokens为false,mExisting为DisplayAreaGroup对象。TaskDIsplayArea和DisplayAreaGroup节点不记录在areaForLayer数组中

/** Adds all {@link TaskDisplayArea} to the application layer. */
        private void addTaskDisplayAreasToApplicationLayer(PendingArea parentPendingArea) {
            final int count = mTaskDisplayAreas.size();
            for (int i = 0; i < count; i++) {
                PendingArea leafArea =
                        new PendingArea(null /* feature */, APPLICATION_LAYER, parentPendingArea);
                leafArea.mExisting = mTaskDisplayAreas.get(i);
                leafArea.mMaxLayer = APPLICATION_LAYER;
                parentPendingArea.mChildren.add(leafArea);
            }
        }

        /** Adds roots of the DisplayAreaGroups to the application layer. */
        private void addDisplayAreaGroupsToApplicationLayer(
                DisplayAreaPolicyBuilder.PendingArea parentPendingArea,
                @Nullable List<HierarchyBuilder> displayAreaGroupHierarchyBuilders) {
            if (displayAreaGroupHierarchyBuilders == null) {
                return;
            }
            final int count = displayAreaGroupHierarchyBuilders.size();
            for (int i = 0; i < count; i++) {
                DisplayAreaPolicyBuilder.PendingArea
                        leafArea = new DisplayAreaPolicyBuilder.PendingArea(
                        null /* feature */, APPLICATION_LAYER, parentPendingArea);
                leafArea.mExisting = displayAreaGroupHierarchyBuilders.get(i).mRoot;
                leafArea.mMaxLayer = APPLICATION_LAYER;
                parentPendingArea.mChildren.add(leafArea);
            }
        }

pendingArea树中的节点类型怎么区分呢?如果pendingArea的PendingArea的mFeature不为null,则表示这是一个feature节点。pendingArea的mSkipTokens 为false,表示 后面转化为DisplayArea树的时候不要为其创建Tokens类型节点,而pendingArea的mExisting 表示 创建该类型的节点。

综上,对于

  1. feature节点:pendingArea 的mFeature不为null
  2. task container节点:mSkipTokens 为false,mExisting =TaskDisplayArea或者DisplayAreaGroup
  3. ime container节点:mSkipTokens 为false,mExisting =mImeContainer。

5.3 构建DisplayArea树

5.1 和5.2 建立的是PendingArea树,我们需要将其转化为DisplayArea树。通过调用该pendingArea树的根节点的instantiateChildren方法,建立DisplayArea树,结果保存在mRoot成员中。

调用 root.instantiateChildren(mRoot, displayAreaForLayer, 0, featureAreas);

建立以mRoot为根的DisplayArea树。输出为mRoot、displayAreaForLayer和featureAreas。displayAreaForLayer为Tokens数组,就像上面pendingArea数组一样,displayAreaForLayer存储生成的DisplayArea树的叶子节点,而featureAreas则是feature到DisplayArea数组的map。

void instantiateChildren(DisplayArea<DisplayArea> parent, DisplayArea.Tokens[] areaForLayer,
                int level, Map<Feature, List<DisplayArea<WindowContainer>>> areas) {
            mChildren.sort(Comparator.comparingInt(pendingArea -> pendingArea.mMinLayer));
            for (int i = 0; i < mChildren.size(); i++) {
                final PendingArea child = mChildren.get(i);
                final DisplayArea area = child.createArea(parent, areaForLayer);
                if (area == null) {
                    // TaskDisplayArea and ImeContainer can be set at different hierarchy, so it can
                    // be null.
                    continue;
                }
                parent.addChild(area, WindowContainer.POSITION_TOP);
                if (child.mFeature != null) {
                    areas.get(child.mFeature).add(area);
                }
                child.instantiateChildren(area, areaForLayer, level + 1, areas);
            }
        }
@Nullable
        private DisplayArea createArea(DisplayArea<DisplayArea> parent,
                DisplayArea.Tokens[] areaForLayer) {
            if (mExisting != null) {
                if (mExisting.asTokens() != null) {
                    // Store the WindowToken container for layers
                    fillAreaForLayers(mExisting.asTokens(), areaForLayer);
                }
                return mExisting;
            }
            if (mSkipTokens) {
                return null;
            }
            DisplayArea.Type type;
            if (mMinLayer > APPLICATION_LAYER) {
                type = DisplayArea.Type.ABOVE_TASKS;
            } else if (mMaxLayer < APPLICATION_LAYER) {
                type = DisplayArea.Type.BELOW_TASKS;
            } else {
                type = DisplayArea.Type.ANY;
            }
            if (mFeature == null) {
                final DisplayArea.Tokens leaf = new DisplayArea.Tokens(parent.mWmService, type,
                        "Leaf:" + mMinLayer + ":" + mMaxLayer);
                fillAreaForLayers(leaf, areaForLayer);
                return leaf;
            } else {
                return mFeature.mNewDisplayAreaSupplier.create(parent.mWmService, type,
                        mFeature.mName + ":" + mMinLayer + ":" + mMaxLayer, mFeature.mId);
            }
        }

在layer 2之上的DisplayArea的type值为ABOVE_TASKS,之下为BELOW_TASKS,相等则type为ANY,表示任何一个TaskDIsplayArea或者DisplayAareaGroup的意思

6 DisplayAreaGroup树

构建方式和DisplayContent树是一样的,都是调用HierarchyBuilder的Builder方法,唯一区别是DisplayAreaGroup树没有传入参数。

Result build(WindowManagerService wmService) {
        validate();

        // Attach DA group roots to screen hierarchy before adding windows to group hierarchies.
        mRootHierarchyBuilder.build(mDisplayAreaGroupHierarchyBuilders);
        List<RootDisplayArea> displayAreaGroupRoots = new ArrayList<>(
                mDisplayAreaGroupHierarchyBuilders.size());
        for (int i = 0; i < mDisplayAreaGroupHierarchyBuilders.size(); i++) {
            HierarchyBuilder hierarchyBuilder = mDisplayAreaGroupHierarchyBuilders.get(i);
            hierarchyBuilder.build();
            displayAreaGroupRoots.add(hierarchyBuilder.mRoot);
        }
        // Use the default function if it is not specified otherwise.
        if (mSelectRootForWindowFunc == null) {
            mSelectRootForWindowFunc = new DefaultSelectRootForWindowFunction(
                    mRootHierarchyBuilder.mRoot, displayAreaGroupRoots);
        }
        return new Result(wmService, mRootHierarchyBuilder.mRoot, displayAreaGroupRoots,
                mSelectRootForWindowFunc);
    }

窗口层次结构,每一个元素的类型都是DisplayArea。具体有TaskDisplayArea,DisplayAreaGroup、DisplayArea.Tokens(非TaskDisplayArea和DisplayAreaGroup节点)和DisplayArea(feature节点)。而在application_layer左边layer的节点叫做BELOW_TASKS, 右边叫做ABOVE_TASKS。而application_layer上的节点叫做any.每一个DisplayAreaGroup单独建立一个DisplayAreaGroup树,DisplayAreaPolicyBuilder设置其RootHierarchy(DisplayContent树)和众多DisplayAreaGroupHierarchy(DisplayAreaGroup树),每一个HierarchyBuilder都以其mRoot为根建立hierarchy,有几个HierarchyBuilder就有几棵树。

mSelectRootForWindowFunc是为了在window创建的时候却定该window应该在哪一颗树下。一般采用默认实现DefaultSelectRootForWindowFunction 。传入windowType和featureID,在RootDisplayArea中找mFeatureId和它相同的RootDisplayArea。

/**
     * When a window is created, the policy will use this function, which takes window type and
     * options, to select the {@link RootDisplayArea} to place that window in. The selected root
     * can be either the one of the {@link #mRootHierarchyBuilder} or the one of any of the
     * {@link #mDisplayAreaGroupHierarchyBuilders}.
     * @see DefaultSelectRootForWindowFunction as an example.
     **/
    @Nullable private BiFunction<Integer, Bundle, RootDisplayArea> mSelectRootForWindowFunc;

第一个参数为windowtype,第二个参数为包含有featureID,最后一个为返回的RootDisplayArea。

/**
     * The default function to be used for finding {@link RootDisplayArea} for window to be attached
     * to if there is no other function set through {@link #setSelectRootForWindowFunc(BiFunction)}.
     *
     * When a window is created with {@link Bundle} options, this function will select the
     * {@link RootDisplayArea} based on the options. Returns {@link #mDisplayRoot} if there is no
     * match found.
     */
    private static class DefaultSelectRootForWindowFunction implements
            BiFunction<Integer, Bundle, RootDisplayArea> {
        final RootDisplayArea mDisplayRoot;
        final List<RootDisplayArea> mDisplayAreaGroupRoots;

        DefaultSelectRootForWindowFunction(RootDisplayArea displayRoot,
                List<RootDisplayArea> displayAreaGroupRoots) {
            mDisplayRoot = displayRoot;
            mDisplayAreaGroupRoots = Collections.unmodifiableList(displayAreaGroupRoots);
        }

        @Override
        public RootDisplayArea apply(Integer windowType, Bundle options) {
            if (mDisplayAreaGroupRoots.isEmpty()) {
                return mDisplayRoot;
            }

            // Select the RootDisplayArea set in options.
            if (options != null && options.containsKey(KEY_ROOT_DISPLAY_AREA_ID)) {
                final int rootId = options.getInt(KEY_ROOT_DISPLAY_AREA_ID);
                if (mDisplayRoot.mFeatureId == rootId) {
                    return mDisplayRoot;
                }
                for (int i = mDisplayAreaGroupRoots.size() - 1; i >= 0; i--) {
                    if (mDisplayAreaGroupRoots.get(i).mFeatureId == rootId) {
                        return mDisplayAreaGroupRoots.get(i);
                    }
                }
            }
            return mDisplayRoot;
        }
    }

7 DisplayAreaPolicy

最后返回DisplayAreaPolicy,这是一个抽象类,实现类为DisplayAreaPolicyBuilder.Result.

static class Result extends DisplayAreaPolicy {
        final List<RootDisplayArea> mDisplayAreaGroupRoots;
        final BiFunction<Integer, Bundle, RootDisplayArea> mSelectRootForWindowFunc;
        private final TaskDisplayArea mDefaultTaskDisplayArea;

        Result(WindowManagerService wmService, RootDisplayArea root,
                List<RootDisplayArea> displayAreaGroupRoots,
                BiFunction<Integer, Bundle, RootDisplayArea> selectRootForWindowFunc) {
            super(wmService, root);
            mDisplayAreaGroupRoots = Collections.unmodifiableList(displayAreaGroupRoots);
            mSelectRootForWindowFunc = selectRootForWindowFunc;

            // Cache the default TaskDisplayArea for quick access.
            mDefaultTaskDisplayArea = mRoot.getItemFromTaskDisplayAreas(taskDisplayArea ->
                    taskDisplayArea.mFeatureId == FEATURE_DEFAULT_TASK_CONTAINER
                            ? taskDisplayArea
                            : null);
            if (mDefaultTaskDisplayArea == null) {
                throw new IllegalStateException(
                        "No display area with FEATURE_DEFAULT_TASK_CONTAINER");
            }
        }
**
 * Policy that manages {@link DisplayArea}.
 */
public abstract class DisplayAreaPolicy {
    protected final WindowManagerService mWmService;

    /**
     * The {@link RootDisplayArea} of the whole logical display. All {@link DisplayArea}s must be
     * (direct or indirect) descendants of this area.
     */
    protected final RootDisplayArea mRoot;

    /**
     * Constructs a new {@link DisplayAreaPolicy}
     *
     * @param wmService the window manager service instance
     * @param root the root display area under which the policy operates
     */
    protected DisplayAreaPolicy(WindowManagerService wmService, RootDisplayArea root) {
        mWmService = wmService;
        mRoot = root;
    }

核心成员为mRoot(DisplayContent树)、mDisplayAreaGroupRoots(DisplayAreaGroup树)、mSelectRootForWindowFunc、mDefaultTaskDisplayArea。

mDefaultTaskDisplayArea为了快速访问而存在,

// Cache the default TaskDisplayArea for quick access.
            mDefaultTaskDisplayArea = mRoot.getItemFromTaskDisplayAreas(taskDisplayArea ->
                    taskDisplayArea.mFeatureId == FEATURE_DEFAULT_TASK_CONTAINER
                            ? taskDisplayArea
                            : null);

8 Root Home Task

ATMS的mRootWindowContainer调用setWindowManager创建DisplayContent后,再调用getDefaultTaskDisplayArea获取默认的TaskDisplayArea,然后调用其getOrCreateRootHomeTask方法。

从DisplayManagerService中获取屏幕集合,然后为每个屏幕创建DisplayContent

```cpp
void setWindowManager(WindowManagerService wm) {
        mWindowManager = wm;
        mDisplayManager = mService.mContext.getSystemService(DisplayManager.class);
        mDisplayManager.registerDisplayListener(this, mService.mUiHandler);
        mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);

        final Display[] displays = mDisplayManager.getDisplays();
        for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) {
            final Display display = displays[displayNdx];
            final DisplayContent displayContent = new DisplayContent(display, this);
            addChild(displayContent, POSITION_BOTTOM);
//这里----------设置了mDefaultDisplay 
            if (displayContent.mDisplayId == DEFAULT_DISPLAY) {
                mDefaultDisplay = displayContent;
            }
        }
        calculateDefaultMinimalSizeOfResizeableTasks();

        final TaskDisplayArea defaultTaskDisplayArea = getDefaultTaskDisplayArea();
//这里------------------------------------------------
        defaultTaskDisplayArea.getOrCreateRootHomeTask(ON_TOP);
        positionChildAt(POSITION_TOP, defaultTaskDisplayArea.mDisplayContent,
                false /* includingParents */);
    }

```cpp
/* Get the default display area on the device dedicated to app windows. This one should be used
     * only as a fallback location for activity launches when no target display area is specified,
     * or for cases when multi-instance is not supported yet (like Split-screen, Freeform, PiP or
     * Recents).
     */
    TaskDisplayArea getDefaultTaskDisplayArea() {
        return mDefaultDisplay.getDefaultTaskDisplayArea();
    }

mDefaultDisplay是DisplayContent类型。

TaskDisplayArea getDefaultTaskDisplayArea() {
        return mDisplayAreaPolicy.getDefaultTaskDisplayArea();
    }
@Override
        public TaskDisplayArea getDefaultTaskDisplayArea() {
            return mDefaultTaskDisplayArea;
        }

其本质就是返回DisplayAreaPolicyBuilder.Result的mDefaultTaskDisplayArea成员,该成员的设置第7节以及说过。

现在看看

defaultTaskDisplayArea.getOrCreateRootHomeTask(ON_TOP);

@Nullable
    Task getOrCreateRootHomeTask() {
        return getOrCreateRootHomeTask(false /* onTop */);
    }
/**
     * Returns the existing root home task or creates and returns a new one if it should exist
     * for the display.
     *
     * @param onTop Only be used when there is no existing root home task. If true the root home
     *              task will be created at the top of the display, else at the bottom.
     */
    @Nullable
    Task getOrCreateRootHomeTask(boolean onTop) {
        Task homeTask = getRootHomeTask();
        // Take into account if this TaskDisplayArea can have a home task before trying to
        // create the root task
        if (homeTask == null && canHostHomeTask()) {
            homeTask = createRootTask(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, onTop);
        }
        return homeTask;
    }
@Nullable
    Task getRootHomeTask() {
        return mRootHomeTask;
    }
   /**
     * True if this TaskDisplayArea can have a home task
     * {@link WindowConfiguration#ACTIVITY_TYPE_HOME}
     */
    private final boolean mCanHostHomeTask;
TaskDisplayArea(DisplayContent displayContent, WindowManagerService service, String name,
                    int displayAreaFeature, boolean createdByOrganizer) {
        this(displayContent, service, name, displayAreaFeature, createdByOrganizer,
                true /* canHostHomeTask */);
    }

    TaskDisplayArea(DisplayContent displayContent, WindowManagerService service, String name,
                    int displayAreaFeature, boolean createdByOrganizer,
                    boolean canHostHomeTask) {
        super(service, Type.ANY, name, displayAreaFeature);
        mDisplayContent = displayContent;
        mRootWindowContainer = service.mRoot;
        mAtmService = service.mAtmService;
        mCreatedByOrganizer = createdByOrganizer;
        mCanHostHomeTask = canHostHomeTask; //为true
    }

创建mRootHomeTask

Task createRootTask(int windowingMode, int activityType, boolean onTop) {
        return createRootTask(windowingMode, activityType, onTop, null /* activityOptions */);
    }
/**
     * A convinenit method of creating a root task by providing windowing mode and activity type
     * on this display.
     *
     * @param windowingMode      The windowing mode the root task should be created in. If
     *                           {@link WindowConfiguration#WINDOWING_MODE_UNDEFINED} then the
     *                           root task will inherit its parent's windowing mode.
     * @param activityType       The activityType the root task should be created in. If
     *                           {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} then the
     *                           root task will be created in
     *                           {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}.
     * @param onTop              If true the root task will be created at the top of the display,
     *                           else at the bottom.
     * @param opts               The activity options.
     * @return The newly created root task.
     */
    Task createRootTask(int windowingMode, int activityType, boolean onTop, ActivityOptions opts) {
        return new Task.Builder(mAtmService)
                .setWindowingMode(windowingMode)
                .setActivityType(activityType)
                .setParent(this)
                .setOnTop(onTop)
                .setActivityOptions(opts)
                .build();
    }
Task build() {
            if (mParent != null && mParent instanceof TaskDisplayArea) {
                validateRootTask((TaskDisplayArea) mParent);
            }

            if (mActivityInfo == null) {
                mActivityInfo = new ActivityInfo();
                mActivityInfo.applicationInfo = new ApplicationInfo();
            }

            mUserId = UserHandle.getUserId(mActivityInfo.applicationInfo.uid);
            mTaskAffiliation = mTaskId;
            mLastTimeMoved = System.currentTimeMillis();
            mNeverRelinquishIdentity = true;
            mCallingUid = mActivityInfo.applicationInfo.uid;
            mCallingPackage = mActivityInfo.packageName;
            mResizeMode = mActivityInfo.resizeMode;
            mSupportsPictureInPicture = mActivityInfo.supportsPictureInPicture();
            if (mActivityOptions != null) {
                mRemoveWithTaskOrganizer = mActivityOptions.getRemoveWithTaskOranizer();
            }

            final Task task = buildInner();
            task.mHasBeenVisible = mHasBeenVisible;

            // Set activity type before adding the root task to TaskDisplayArea, so home task can
            // be cached, see TaskDisplayArea#addRootTaskReferenceIfNeeded().
            if (mActivityType != ACTIVITY_TYPE_UNDEFINED) {
                task.setActivityType(mActivityType);
            }

            if (mParent != null) {
                if (mParent instanceof Task) {
                    final Task parentTask = (Task) mParent;
                    parentTask.addChild(task, mOnTop ? POSITION_TOP : POSITION_BOTTOM,
                            (mActivityInfo.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
                } else {
                    mParent.addChild(task, mOnTop ? POSITION_TOP : POSITION_BOTTOM);
                }
            }

            // Set windowing mode after attached to display area or it abort silently.
            if (mWindowingMode != WINDOWING_MODE_UNDEFINED) {
                task.setWindowingMode(mWindowingMode, true /* creating */);
            }
            return task;
        }

将创建的Root Home Task加入TaskDiplayArea中。

9 将mDefaultDisplay作为最右孩子添加到RootWindowContainer.mChildren中

void setWindowManager(WindowManagerService wm) {
        mWindowManager = wm;
        mDisplayManager = mService.mContext.getSystemService(DisplayManager.class);
        mDisplayManager.registerDisplayListener(this, mService.mUiHandler);
        mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);

        final Display[] displays = mDisplayManager.getDisplays();
        for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) {
            final Display display = displays[displayNdx];
            final DisplayContent displayContent = new DisplayContent(display, this);
            addChild(displayContent, POSITION_BOTTOM);

            if (displayContent.mDisplayId == DEFAULT_DISPLAY) {
                mDefaultDisplay = displayContent;
            }
        }
        calculateDefaultMinimalSizeOfResizeableTasks();

        final TaskDisplayArea defaultTaskDisplayArea = getDefaultTaskDisplayArea();

        defaultTaskDisplayArea.getOrCreateRootHomeTask(ON_TOP);
//这里---------------------------------
        positionChildAt(POSITION_TOP, defaultTaskDisplayArea.mDisplayContent,
                false /* includingParents */);
    }

调用父类WindowContainer的方法

```cpp
/**
     * Move a child from it's current place in siblings list to the specified position,
     * with an option to move all its parents to top.
     * @param position Target position to move the child to.
     * @param child Child to move to selected position.
     * @param includingParents Flag indicating whether we need to move the entire branch of the
     *                         hierarchy when we're moving a child to {@link #POSITION_TOP} or
     *                         {@link #POSITION_BOTTOM}. When moving to other intermediate positions
     *                         this flag will do nothing.
     */
    @CallSuper
    void positionChildAt(int position, E child, boolean includingParents) {
        if (child.getParent() != this) {
            throw new IllegalArgumentException("positionChildAt: container=" + child.getName()
                    + " is not a child of container=" + getName()
                    + " current parent=" + child.getParent());
        }

        if (position >= mChildren.size() - 1) {
            position = POSITION_TOP;
        } else if (position <= 0) {
            position = POSITION_BOTTOM;
        }

        switch (position) {
            case POSITION_TOP:
                if (mChildren.peekLast() != child) {
                    mChildren.remove(child);
                    mChildren.add(child);
                    onChildPositionChanged(child);
                }
                if (includingParents && getParent() != null) {
                    getParent().positionChildAt(POSITION_TOP, this /* child */,
                            true /* includingParents */);
                }
                break;
            case POSITION_BOTTOM:
                if (mChildren.peekFirst() != child) {
                    mChildren.remove(child);
                    mChildren.addFirst(child);
                    onChildPositionChanged(child);
                }
                if (includingParents && getParent() != null) {
                    getParent().positionChildAt(POSITION_BOTTOM, this /* child */,
                            true /* includingParents */);
                }
                break;
            default:
                // TODO: Removing the child before reinserting requires the caller to provide a
                //       position that takes into account the removed child (if the index of the
                //       child < position, then the position should be adjusted). We should consider
                //       doing this adjustment here and remove any adjustments in the callers.
                if (mChildren.indexOf(child) != position) {
                    mChildren.remove(child);
                    mChildren.add(position, child);
                    onChildPositionChanged(child);
                }
        }
    }

10 各类窗口添加到DisplayContent树

10**.1 非Activity的窗口作为DisplayArea.Tokens的孩子添加到wm层次结构**

如:WallpaperWindowToken加入到Leaf0:1,AssistPreviewPanel DockedStackDivider加入到Leaf3:14,RoundCorner NavigationBar StatusBar等加入到Leaf17:34。它们都是非Activity的窗口,这些窗口在创建时,进行添加:

protected WindowToken(WindowManagerService service, IBinder _token, int type,
            boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens,
            boolean roundedCornerOverlay, boolean fromClientToken, @Nullable Bundle options) {
        super(service);
        token = _token;
        windowType = type;
        mOptions = options;
        mPersistOnEmpty = persistOnEmpty;
        mOwnerCanManageAppTokens = ownerCanManageAppTokens;
        mRoundedCornerOverlay = roundedCornerOverlay;
        mFromClientToken = fromClientToken;
        if (dc != null) {
//这里---------------------
            dc.addWindowToken(token, this);
        }
    }
void addWindowToken(IBinder binder, WindowToken token) {
        final DisplayContent dc = mWmService.mRoot.getWindowTokenDisplay(token);
        if (dc != null) {
            // We currently don't support adding a window token to the display if the display
            // already has the binder mapped to another token. If there is a use case for supporting
            // this moving forward we will either need to merge the WindowTokens some how or have
            // the binder map to a list of window tokens.
            throw new IllegalArgumentException("Can't map token=" + token + " to display="
                    + getName() + " already mapped to display=" + dc + " tokens=" + dc.mTokenMap);
        }
        if (binder == null) {
            throw new IllegalArgumentException("Can't map token=" + token + " to display="
                    + getName() + " binder is null");
        }
        if (token == null) {
            throw new IllegalArgumentException("Can't map null token to display="
                    + getName() + " binder=" + binder);
        }

        mTokenMap.put(binder, token);
//非Activity窗口加入到DisplayContent树种
        if (token.asActivityRecord() == null) {
            // Set displayContent for non-app token to prevent same token will add twice after
            // onDisplayChanged.
            // TODO: Check if it's fine that super.onDisplayChanged of WindowToken
            //  (WindowsContainer#onDisplayChanged) may skipped when token.mDisplayContent assigned.
            token.mDisplayContent = this;
            // Add non-app token to container hierarchy on the display. App tokens are added through
            // the parent container managing them (e.g. Tasks).
   //findAreaForToken找到该WindowToken应该要挂的DisplayArea
            final DisplayArea.Tokens da = findAreaForToken(token).asTokens();
            da.addChild(token);
        }
    }
/**
     * Finds the {@link DisplayArea} for the {@link WindowToken} to attach to.
     * <p>
     * Note that the differences between this API and
     * {@link RootDisplayArea#findAreaForTokenInLayer(WindowToken)} is that this API finds a
     * {@link DisplayArea} in {@link DisplayContent} level, which may find a {@link DisplayArea}
     * from multiple {@link RootDisplayArea RootDisplayAreas} under this {@link DisplayContent}'s
     * hierarchy, while {@link RootDisplayArea#findAreaForTokenInLayer(WindowToken)} finds a
     * {@link DisplayArea.Tokens} from a {@link DisplayArea.Tokens} list mapped to window layers.
     * </p>
     *
     * @see DisplayContent#findAreaForTokenInLayer(WindowToken)
     */
    DisplayArea findAreaForToken(WindowToken windowToken) {
        return findAreaForWindowType(windowToken.getWindowType(), windowToken.mOptions,
                windowToken.mOwnerCanManageAppTokens, windowToken.mRoundedCornerOverlay);
    }
DisplayArea findAreaForWindowType(int windowType, Bundle options,
            boolean ownerCanManageAppToken, boolean roundedCornerOverlay) {
        // TODO(b/159767464): figure out how to find an appropriate TDA.
        if (windowType >= FIRST_APPLICATION_WINDOW && windowType <= LAST_APPLICATION_WINDOW) {
            return getDefaultTaskDisplayArea();
        }
        // Return IME container here because it could be in one of sub RootDisplayAreas depending on
        // the focused edit text. Also, the RootDisplayArea choosing strategy is implemented by
        // the server side, but not mSelectRootForWindowFunc customized by OEM.
        if (windowType == TYPE_INPUT_METHOD || windowType == TYPE_INPUT_METHOD_DIALOG) {
            return getImeContainer();
        }
        return mDisplayAreaPolicy.findAreaForWindowType(windowType, options,
                ownerCanManageAppToken, roundedCornerOverlay);
    }

输入法窗口添加到mImeWindowsContainers,其他则调用mDisplayAreaPolicy

@Override
        public DisplayArea.Tokens findAreaForWindowType(int type, Bundle options,
                boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) {
            return mSelectRootForWindowFunc.apply(type, options).findAreaForWindowTypeInLayer(type,
                    ownerCanManageAppTokens, roundedCornerOverlay);
        }

mSelectRootForWindowFunc用于寻找windowToken应该挂载的DisplayArea树的root。它的产生过程如下:

Result build(WindowManagerService wmService) {
        validate();

        // Attach DA group roots to screen hierarchy before adding windows to group hierarchies.
        mRootHierarchyBuilder.build(mDisplayAreaGroupHierarchyBuilders);
        List<RootDisplayArea> displayAreaGroupRoots = new ArrayList<>(
                mDisplayAreaGroupHierarchyBuilders.size());
        for (int i = 0; i < mDisplayAreaGroupHierarchyBuilders.size(); i++) {
            HierarchyBuilder hierarchyBuilder = mDisplayAreaGroupHierarchyBuilders.get(i);
            hierarchyBuilder.build();
            displayAreaGroupRoots.add(hierarchyBuilder.mRoot);
        }
        // Use the default function if it is not specified otherwise.
        if (mSelectRootForWindowFunc == null) {
//这里-------------
            mSelectRootForWindowFunc = new DefaultSelectRootForWindowFunction(
                    mRootHierarchyBuilder.mRoot, displayAreaGroupRoots);
        }
        return new Result(wmService, mRootHierarchyBuilder.mRoot, displayAreaGroupRoots,
                mSelectRootForWindowFunc);
    }
/* The default function to be used for finding {@link RootDisplayArea} for window to be attached
     * to if there is no other function set through {@link #setSelectRootForWindowFunc(BiFunction)}.
     *
     * When a window is created with {@link Bundle} options, this function will select the
     * {@link RootDisplayArea} based on the options. Returns {@link #mDisplayRoot} if there is no
     * match found.
     */
    private static class DefaultSelectRootForWindowFunction implements
            BiFunction<Integer, Bundle, RootDisplayArea> {
        final RootDisplayArea mDisplayRoot;
        final List<RootDisplayArea> mDisplayAreaGroupRoots;

        DefaultSelectRootForWindowFunction(RootDisplayArea displayRoot,
                List<RootDisplayArea> displayAreaGroupRoots) {
            mDisplayRoot = displayRoot;
            mDisplayAreaGroupRoots = Collections.unmodifiableList(displayAreaGroupRoots);
        }

        @Override
        public RootDisplayArea apply(Integer windowType, Bundle options) {
            if (mDisplayAreaGroupRoots.isEmpty()) {
                return mDisplayRoot;
            }

            // Select the RootDisplayArea set in options.
            if (options != null && options.containsKey(KEY_ROOT_DISPLAY_AREA_ID)) {
                final int rootId = options.getInt(KEY_ROOT_DISPLAY_AREA_ID);
                if (mDisplayRoot.mFeatureId == rootId) {
                    return mDisplayRoot;
                }
                for (int i = mDisplayAreaGroupRoots.size() - 1; i >= 0; i--) {
                    if (mDisplayAreaGroupRoots.get(i).mFeatureId == rootId) {
                        return mDisplayAreaGroupRoots.get(i);
                    }
                }
            }
            return mDisplayRoot;
        }
    }

如果option设置了KEY_ROOT_DISPLAY_AREA_ID则根据这个获取root,否则返回mDisplayRoot,即DisplayContent

10.2 Activity的窗口作为defaultTaskDisplayArea的孩子添加到wm层次结构

ActivityRecord extends WindowToken

好的,下面是一个简单的Java图书信息管理系统窗口版代码示例: ```java import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; public class BookManager extends JFrame implements ActionListener { private JLabel titleLabel, idLabel, nameLabel, authorLabel, publisherLabel, yearLabel; private JTextField idField, nameField, authorField, publisherField, yearField; private JButton addButton, deleteButton, updateButton, searchButton, clearButton; private JTextArea displayArea; private ArrayList<Book> bookList; public BookManager() { super("图书信息管理系统"); // 初始化组件 titleLabel = new JLabel("图书信息管理系统", JLabel.CENTER); titleLabel.setFont(new Font("楷体", Font.BOLD, 20)); idLabel = new JLabel("编号:"); nameLabel = new JLabel("书名:"); authorLabel = new JLabel("作者:"); publisherLabel = new JLabel("出版社:"); yearLabel = new JLabel("出版年份:"); idField = new JTextField(10); nameField = new JTextField(10); authorField = new JTextField(10); publisherField = new JTextField(10); yearField = new JTextField(10); addButton = new JButton("添加"); deleteButton = new JButton("删除"); updateButton = new JButton("修改"); searchButton = new JButton("查找"); clearButton = new JButton("清空"); displayArea = new JTextArea(10, 30); displayArea.setEditable(false); // 设置布局 Container container = getContentPane(); container.setLayout(new BorderLayout()); JPanel topPanel = new JPanel(new GridLayout(1, 1)); topPanel.add(titleLabel); container.add(topPanel, BorderLayout.NORTH); JPanel centerPanel = new JPanel(new GridLayout(5, 2)); centerPanel.add(idLabel); centerPanel.add(idField); centerPanel.add(nameLabel); centerPanel.add(nameField); centerPanel.add(authorLabel); centerPanel.add(authorField); centerPanel.add(publisherLabel); centerPanel.add(publisherField); centerPanel.add(yearLabel); centerPanel.add(yearField); container.add(centerPanel, BorderLayout.CENTER); JPanel bottomPanel = new JPanel(new FlowLayout()); bottomPanel.add(addButton); bottomPanel.add(deleteButton); bottomPanel.add(updateButton); bottomPanel.add(searchButton); bottomPanel.add(clearButton); container.add(bottomPanel, BorderLayout.SOUTH); JScrollPane scrollPane = new JScrollPane(displayArea); container.add(scrollPane, BorderLayout.EAST); // 添加事件监听 addButton.addActionListener(this); deleteButton.addActionListener(this); updateButton.addActionListener(this); searchButton.addActionListener(this); clearButton.addActionListener(this); // 初始化图书列表 bookList = new ArrayList<>(); } public void actionPerformed(ActionEvent e) { if (e.getSource() == addButton) { // 添加图书 String id = idField.getText(); String name = nameField.getText(); String author = authorField.getText(); String publisher = publisherField.getText(); String year = yearField.getText(); Book book = new Book(id, name, author, publisher, year); bookList.add(book); displayArea.append("添加成功:" + book + "\n"); clearFields(); } else if (e.getSource() == deleteButton) { // 删除图书 String id = idField.getText(); boolean found = false; for (int i = 0; i < bookList.size(); i++) { Book book = bookList.get(i); if (book.getId().equals(id)) { bookList.remove(i); displayArea.append("删除成功:" + book + "\n"); found = true; break; } } if (!found) { displayArea.append("找不到编号为" + id + "的图书\n"); } clearFields(); } else if (e.getSource() == updateButton) { // 修改图书 String id = idField.getText(); boolean found = false; for (int i = 0; i < bookList.size(); i++) { Book book = bookList.get(i); if (book.getId().equals(id)) { String name = nameField.getText(); String author = authorField.getText(); String publisher = publisherField.getText(); String year = yearField.getText(); book.setName(name); book.setAuthor(author); book.setPublisher(publisher); book.setYear(year); displayArea.append("修改成功:" + book + "\n"); found = true; break; } } if (!found) { displayArea.append("找不到编号为" + id + "的图书\n"); } clearFields(); } else if (e.getSource() == searchButton) { // 查找图书 String id = idField.getText(); boolean found = false; for (int i = 0; i < bookList.size(); i++) { Book book = bookList.get(i); if (book.getId().equals(id)) { displayArea.append("查找成功:" + book + "\n"); found = true; break; } } if (!found) { displayArea.append("找不到编号为" + id + "的图书\n"); } clearFields(); } else if (e.getSource() == clearButton) { // 清空文本框 clearFields(); } } private void clearFields() { idField.setText(""); nameField.setText(""); authorField.setText(""); publisherField.setText(""); yearField.setText(""); } public static void main(String[] args) { BookManager bookManager = new BookManager(); bookManager.setSize(600, 400); bookManager.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); bookManager.setVisible(true); } } class Book { private String id; private String name; private String author; private String publisher; private String year; public Book(String id, String name, String author, String publisher, String year) { this.id = id; this.name = name; this.author = author; this.publisher = publisher; this.year = year; } public String getId() { return id; } public String getName() { return name; } public String getAuthor() { return author; } public String getPublisher() { return publisher; } public String getYear() { return year; } public void setName(String name) { this.name = name; } public void setAuthor(String author) { this.author = author; } public void setPublisher(String publisher) { this.publisher = publisher; } public void setYear(String year) { this.year = year; } @Override public String toString() { return "编号:" + id + ", 书名:" + name + ", 作者:" + author + ", 出版社:" + publisher + ", 出版年份:" + year; } } ``` 这个程序使用了Java的Swing库来创建窗口界面,实现了添加、删除、修改、查找和清空等功能。运行程序后,可以在窗口中输入图书信息,然后点击相应的按钮执行相应的操作,并在显示区域中显示执行结果。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值