1. WMS 服务进程启动
1.1 WMS 服务进程启动,创建 RootWindowContainer 实例
SystemServer 进程的 startOtherServices 方法中先后启动了 AMS、WMS 服务:
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
...
// 启动 WMS 服务进程,在启动 AMS 之后
mSystemServiceManager.startBootPhase(t, SystemService.PHASE_WAIT_FOR_SENSOR_SERVICE);
wm = WindowManagerService.main(context, inputManager, !mFirstBoot,
new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
// 关注点,向 AMS 添加 WMS 实例
mActivityManagerService.setWindowManager(wm);
...
}
启动 WMS 服务进程,在 WMS 的构造方法中,创建 RootWindowContainer 实例,RootWindowContainer 是窗口管理树的根节点,并创建了 DisplayAreaPolicy.Provider 对象:
private WindowManagerService(Context context, InputManagerService inputManager,
boolean showBootMsgs, WindowManagerPolicy policy, ActivityTaskManagerService atm,
DisplayWindowSettingsProvider displayWindowSettingsProvider,
Supplier<SurfaceControl.Transaction> transactionFactory,
Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
mRoot = new RootWindowContainer(this);
// 实际类型是 DisplayAreaPolicy.DefaultProvider
mDisplayAreaPolicyProvider = DisplayAreaPolicy.Provider.fromResources(mContext.getResources());
}
1.2 WMS 服务进程启动,AMS、ATMS、WMS、RootWindowContainer 之间建立联系
接着调用了 AMS 的 setWindowManager 方法向 AMS 添加 WMS 实例:
public void setWindowManager(WindowManagerService wm) {
synchronized (this) {
mWindowManager = wm;
mWmInternal = LocalServices.getService(WindowManagerInternal.class);
// 向 ATMS 添加 WMS 实例
mActivityTaskManager.setWindowManager(wm);
}
}
向 ATMS 添加 WMS 实例:
// frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
public void setWindowManager(com.android.server.wm.WindowManagerService wm) {
synchronized (mGlobalLock) {
mWindowManager = wm;
// ATMS 持有了 RootWindowContainer 实例
mRootWindowContainer = wm.mRoot;
mWindowOrganizerController.mTransitionController.setWindowManager(wm);
mLifecycleManager.setWindowManager(wm);
mTempConfig.setToDefaults();
mTempConfig.setLocales(LocaleList.getDefault());
mConfigurationSeq = mTempConfig.seq = 1;
mRootWindowContainer.onConfigurationChanged(mTempConfig);
mLockTaskController.setWindowManager(wm);
mTaskSupervisor.setWindowManager(wm);
// 从 mRootWindowContainer.setWindowManager(wm) 开始,就开始了窗口容器树的构建过程
mRootWindowContainer.setWindowManager(wm);
mBackNavigationController.setWindowManager(wm);
}
}
将 WMS 持有的 RootWindowContainer 实例赋值给了 ATMS 的 mRootWindowContainer。接着就调用了 mRootWindowContainer 的 setWindowManager 方法,此时就开始了窗口容器树的构建过程。
2. RootWindowContaine,开始构建窗口容器树
2.1 ATMS 中调用 RootWindowContainer setWindowManager 方法,开始窗口容器树的构建流程
看一下 RootWindowContainer 的 setWindowManager 方法:
void setWindowManager(WindowManagerService wm) {
// 将 WMS 实例传递给 RootWindowContainer
mWindowManager = wm;
mDisplayManager = mService.mContext.getSystemService(DisplayManager.class);
mDisplayManager.registerDisplayListener(this, mService.mUiHandler);
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
// 获取屏幕个数
final Display[] displays = mDisplayManager.getDisplays();
// 遍历每一个 Display,为每一个 Display 构建一个 DisplayContent。
for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) {
final Display display = displays[displayNdx];
// 为每一个 Display 创建一个 DisplayContent 对象。在窗口容器树中,DisplayContent 就代表了一个屏幕。
final DisplayContent displayContent = new DisplayContent(display, this, mDeviceStateController);
addChild(displayContent, POSITION_BOTTOM);
// DEFAULT_DISPLAY 0 默认的主屏
if (displayContent.mDisplayId == DEFAULT_DISPLAY) {
mDefaultDisplay = displayContent;
}
}
final TaskDisplayArea defaultTaskDisplayArea = getDefaultTaskDisplayArea();
defaultTaskDisplayArea.getOrCreateRootHomeTask(ON_TOP);
positionChildAt(POSITION_TOP, defaultTaskDisplayArea.mDisplayContent,
false /* includingParents */);
}
通过 DisplayService 获取到了当前设备所有屏幕,接着遍历每一个 Display(屏幕),为每一个 Display 构建一个 DisplayContent。在窗口容器树中,DisplayContent 就代表了一个屏幕。
2.2 DisplayContent 初始化
看一下 DisplayContent 初始化:
DisplayContent(Display display, RootWindowContainer root,
@NonNull DeviceStateController deviceStateController) {
super(root.mWindowManager, "DisplayContent", FEATURE_ROOT);
...
// 创建事务
final Transaction pendingTransaction = getPendingTransaction();
// 开始构建层级树
configureSurfaces(pendingTransaction);
// 执行事务
pendingTransaction.apply();
...
}
看一下 DisplayContent 的 configureSurfaces 方法:
private void configureSurfaces(Transaction transaction) {
// 构建一个 SurfaceControl,表示 SurfaceFlinger 中的一个图层节点
final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(mSession)
.setOpaque(true)
.setContainerLayer()
.setCallsite("DisplayContent");
// 设置名字后构建 (Display 0 name="XXX")Display 0 name="Built-in Screen"
mSurfaceControl = b.setName(getName()).setContainerLayer().build();
if (mDisplayAreaPolicy == null) {
// Setup the policy and build the display area hierarchy.
// Build the hierarchy only after creating the surface so it is reparented correctly
// 关注点,构建树结构。返回一个 DisplayAreaPolicy.Provider 对象
// 构建 DisplayArea 树结构,为不同窗口类型(如状态栏、对话框)分配层级。
mDisplayAreaPolicy = mWmService.getDisplayAreaPolicyProvider().instantiate(
mWmService, this /* content */, this /* root */,
mImeWindowsContainer);
}
final List<DisplayArea<? extends WindowContainer>> areas =
mDisplayAreaPolicy.getDisplayAreas(FEATURE_WINDOWED_MAGNIFICATION);
final DisplayArea<?> area = areas.size() == 1 ? areas.get(0) : null;
if (area != null && area.getParent() == this) {
// The windowed magnification area should contain all non-overlay windows, so just use
// it as the windowing layer.
mWindowingLayer = area.mSurfaceControl;
transaction.reparent(mWindowingLayer, mSurfaceControl);
} else {
// Need an additional layer for screen level animation, so move the layer containing
// the windows to the new root.
mWindowingLayer = mSurfaceControl;
mSurfaceControl = b.setName("RootWrapper").build();
transaction.reparent(mWindowingLayer, mSurfaceControl)
.show(mWindowingLayer);
}
if (mOverlayLayer == null) {
mOverlayLayer = b.setName("Display Overlays").setParent(mSurfaceControl).build();
} else {
transaction.reparent(mOverlayLayer, mSurfaceControl);
}
if (mInputOverlayLayer == null) {
mInputOverlayLayer = b.setName("Input Overlays").setParent(mSurfaceControl).build();
} else {
transaction.reparent(mInputOverlayLayer, mSurfaceControl);
}
if (mA11yOverlayLayer == null) {
mA11yOverlayLayer =
b.setName("Accessibility Overlays").setParent(mSurfaceControl).build();
} else {
transaction.reparent(mA11yOverlayLayer, mSurfaceControl);
}
// 事务相关设置
transaction
.setLayer(mSurfaceControl, 0)
.setLayerStack(mSurfaceControl, mDisplayId)
.show(mSurfaceControl)
.setLayer(mOverlayLayer, Integer.MAX_VALUE)
.show(mOverlayLayer)
.setLayer(mInputOverlayLayer, Integer.MAX_VALUE - 1)
.show(mInputOverlayLayer)
.setLayer(mA11yOverlayLayer, Integer.MAX_VALUE - 2)
.show(mA11yOverlayLayer);
}
SurfaceControl 用于创建、配置和管理 Surface 的显示属性(如大小、位置、透明度、层级等)。
DisplayAreaPolicy 类负责建立和管理 DisplayArea 的层级结构,定义显示区域的划分策略(如系统区、应用区)。
2.3 构建 HierarchyBuilder,配置 Feature
这里直接调用 DefaultProvider 的 instantiate 进行构建 DisplayArea 树结构,为不同窗口类型(如状态栏、对话框)分配层级:
// 默认显式策略
@Override
public DisplayAreaPolicy instantiate(WindowManagerService wmService,
DisplayContent content, RootDisplayArea root,
DisplayArea.Tokens imeContainer) {
// 创建一个名为 "DefaultTaskDisplayArea" 的对象作为应用窗口的默认容器,应用窗口容器
final TaskDisplayArea defaultTaskDisplayArea = new TaskDisplayArea(content, wmService,
"DefaultTaskDisplayArea", FEATURE_DEFAULT_TASK_CONTAINER);
final List<com.android.server.wm.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.
// 传递 RootDisplayArea(DisplayContent)构建出一个层级树的数据结构,用于后续窗口容器树的构建。
final HierarchyBuilder rootHierarchy = new HierarchyBuilder(root);
// Set the essential containers (even if the display doesn't support IME).
// HierarchyBuilder 设置输入法容器和应用窗口容器
rootHierarchy.setImeContainer(imeContainer).setTaskDisplayAreas(tdaList);
if (content.isTrusted()) {
// Only trusted display can have system decorations.
// 配置层级的支持的 Feature
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 com.android.server.wm.DisplayAreaPolicyBuilder().setRootHierarchy(rootHierarchy).build(wmService);
}
创建一个名为 "DefaultTaskDisplayArea" 的对象作为应用窗口的默认容器,应用窗口容器。
构建出 DisplayContent 的层级树的数据结构 HierarchyBuilder,用于后续窗口容器树的构建。
接着通过 configureTrustedHierarchyBuilder 配置层级的支持的 Feature:
/**
* Feature.mName Feature.mID Feature.mWindowLayers 数组为 true 的区间
* WindowedMagnification FEATURE_WINDOWED_MAGNIFICATION [0,31]
* HideDisplayCutout FEATURE_HIDE_DISPLAY_CUTOUT [0,14], 16, [18,23], [26,35]
* OneHanded FEATURE_ONE_HANDED [0,23], [26,32],[34,35]
* FullscreenMagnification FEATURE_FULLSCREEN_MAGNIFICATION [0,12], [15,23], [26,27], [29,31], [33,35]
* ImePlaceholder FEATURE_IME_PLACEHOLDER [13,14]
*/
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.
// 通过 Feature.Builder 构造一个 Feature 对象
// 再调用 HierarchyBuilder 对象的 addFeature 方法,将构造好的 Feature 对象保存到 HierarchyBuilder 对象内部的
// ArrayList<DisplayAreaPolicyBuilder.Feature> mFeatures 成员中
// 构造好了名为 WindowedMagnification 的 Feature 对象
rootHierarchy.addFeature(new Feature.Builder(wmService.mPolicy, "WindowedMagnification",
FEATURE_WINDOWED_MAGNIFICATION)
.upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY) // 0-32 true
.except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)// 32 false
.setNewDisplayAreaSupplier(DisplayArea.Dimmable::new)// new 一个 DisplayArea.Dimmable 保存在 Builder 的 mNewDisplayAreaSupplier 成员中
.build()); // 最后调用 build 方法,new 一个 Feature 返回,前面准备好的数据都作为参数传入构造方法,然后保存到 Feature 中
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, "OneHanded",
FEATURE_ONE_HANDED)
.all()
.except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL,
TYPE_SECURE_SYSTEM_OVERLAY)
.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());
}
DisplayArea 节点会有自己的 Feature,来指定节点所包含的特殊功能。配置层级的支持的 Feature。一共有 5 个 Feature :
- WindowedMagnification
- HideDisplayCutout
- OneHanded
- FullscreenMagnification
- ImePlaceholder
2.4 构建 PendingArea 树
继续调用 DisplayAreaPolicyBuilder 的 build 方法:
// 构建 PendingArea 树
// 根据 PendingArea 树构建最终的窗口容器树
Result build(WindowManagerService wmService) {
validate();
mRootHierarchyBuilder.build(mDisplayAreaGroupHierarchyBuilders);
List<RootDisplayArea> displayAreaGroupRoots = new ArrayList<>(
mDisplayAreaGroupHierarchyBuilders.size());
// mDisplayAreaGroupHierarchyBuilders 是一个空列表
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, mSelectTaskDisplayAreaFunc);
}
继续看一下 HierarchyBuilder 的 build 方法,这里主要通过两个循环构建了 PendingArea 树,完成之后再使用 PendingArea 树构建出目标的窗口容器树:
private void build(@Nullable List<HierarchyBuilder> displayAreaGroupHierarchyBuilders) {
// 初始化局部变量
final WindowManagerPolicy policy = mRoot.mWmService.mPolicy;
// 定义最大层级数 37
final int maxWindowLayerCount = policy.getMaxWindowLayer() + 1;
// 存储每个窗口层级对应的 DisplayArea.Tokens 37 个
final DisplayArea.Tokens[] displayAreaForLayer =
new DisplayArea.Tokens[maxWindowLayerCount];
// 存储每个 Feature 对应的 DisplayArea 列表
final Map<Feature, List<DisplayArea<WindowContainer>>> featureAreas =
new ArrayMap<>(mFeatures.size());
for (int i = 0; i < mFeatures.size(); i++) {
featureAreas.put(mFeatures.get(i), new ArrayList<>());
}
// 初始化 PendingArea 数组,用于临时存储每个窗口层级对应的 PendingArea,一共 37 个
PendingArea[] areaForLayer = new PendingArea[maxWindowLayerCount];
final PendingArea root = new PendingArea(null, 0, null);
// areaForLayer 数组全部填充为 root
Arrays.fill(areaForLayer, root);
// 遍历每一个 Feature。
final int size = mFeatures.size();
for (int i = 0; i < size; i++) {// 遍历每一个 feature
final Feature feature = mFeatures.get(i);
PendingArea featureArea = null;// mParent:父节点引用; mChildren:子节点列表; mMaxLayer:覆盖的最大层级。
for (int layer = 0; layer < maxWindowLayerCount; layer++) {
if (feature.mWindowLayers[layer]) {// 检查该 Feature 是否应用于当前层级,WindowedMagnification 0~31 为 true
// 如果现有功能是为前一层的此功能创建的,并且应用于前一层的最后一个功能与应用于当前层的功能相同,那么我们可以重用现有的 DisplayArea(因此它们可以共享相同的父 DisplayArea)
if (featureArea == null || featureArea.mParent != areaForLayer[layer]) { // 当前 feature 对应的 PendingArea 为 null,或者 PendingArea 的父节点与与当前层的 PendingArea 不同
// 在 for 循环中, layer 为 0,此时 feature.mWindowLayers[0] 为 true,featureArea 为 null,那么创建一个 PendingArea 对象 WindowedMagnification:0:0,这个新创建的 WindowedMagnification:0:0 对象的 parent 是构造方法的第三个参数 areaForLayer[0],值为 Root:0:0,接着将这个新创建的 WindowedMagnification:0:0 添加到 areaForLayer[0] Root:0:0 的子节点中,最后将 areaForLayer[0] 指向这个新创建的 WindowedMagnification:0:0。
featureArea = new PendingArea(feature, layer, areaForLayer[layer]);// 新建 feature 的 PendingArea
areaForLayer[layer].mChildren.add(featureArea);
}
areaForLayer[layer] = featureArea;
} else {
featureArea = null;
}
}
}
// 生成叶子节点
PendingArea leafArea = null;
int leafType = LEAF_TYPE_TOKENS;
for (int layer = 0; layer < maxWindowLayerCount; layer++) {
// 获取叶子节点类型
int type = typeOfLayer(policy, layer);
if (leafArea == null || leafArea.mParent != areaForLayer[layer]
|| type != leafType) {
// new 一个 PendingArea 对象,然后添加到 areaForLayer[layer] 的子节点中,成为叶子节点。
leafArea = new PendingArea(null /* feature */, layer, areaForLayer[layer]);
areaForLayer[layer].mChildren.add(leafArea);
leafType = type;
if (leafType == LEAF_TYPE_TASK_CONTAINERS) {
// 如果叶子节点类型是 LEAF_TYPE_IME_CONTAINERS,也就是 layer 等于 2,则做以下处理:
// 给 areaForLayer[layer] 再添加一个叶子节点
addTaskDisplayAreasToApplicationLayer(areaForLayer[layer]);
// // displayAreaGroupHierarchyBuilders是一个空的列表,这个方法就不用管了
addDisplayAreaGroupsToApplicationLayer(areaForLayer[layer],
displayAreaGroupHierarchyBuilders);
// 接着将叶子节点 leafArea 的 mSkipTokens 设置为 true,那么后续在根据PendingArea 树生成 DisplayArea 层级结构的时候,就不会为这个 PendingArea 对象生成一个 DisplayArea 对象了。
leafArea.mSkipTokens = true;
} else if (leafType == LEAF_TYPE_IME_CONTAINERS) {// 如果叶子节点类型是 LEAF_TYPE_TASK_CONTAINERS,也就是 layer 等于 13 或者 14
// 后续根据 PendingArea 生成 DisplayArea.Tokens 的时候,不再为当前节点生成 DisplayArea.Tokens,而是用之前保存在 HierarchyBuilder.mImeContainer 的 ImeContainer
leafArea.mExisting = mImeContainer;
leafArea.mSkipTokens = true;
}
}
// 设置叶子节点的最大层级
leafArea.mMaxLayer = layer;
}
// 接着调用 computeMaxLayer 计算非叶子节点的 mMaxLayer。在此之前,非叶子节点的 mMaxLayer 都是 0,这里调用 computerMaxLayer 方法,将这些值计算出来:
root.computeMaxLayer();
// PendingArea 树就构造好以后,基于 PendingArea 树生成窗口容器树 DisplayAreas
root.instantiateChildren(mRoot, displayAreaForLayer, 0, featureAreas);
mRoot.onHierarchyBuilt(mFeatures, displayAreaForLayer, featureAreas);
}
2.5 基于 PendingArea 树,生成窗口容器树
void instantiateChildren(DisplayArea<DisplayArea> parent, DisplayArea.Tokens[] areaForLayer,
int level, Map<Feature, List<DisplayArea<WindowContainer>>> areas) {
// 1. 子区域按照它们的最小层级进行升序排列
mChildren.sort(Comparator.comparingInt(pendingArea -> pendingArea.mMinLayer));
// 2. 遍历孩子将 PendingArea 转换成 DisplayArea
for (int i = 0; i < mChildren.size(); i++) {
final PendingArea child = mChildren.get(i);
// 将该节点转成对应的 DisplayArea
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;
}
// 将返回的 area 设置为孩子,第一次执行的时候 root 就是 DisplayContent
parent.addChild(area, WindowContainer.POSITION_TOP);
if (child.mFeature != null) {
// 让 Feature 对应的容器里添加创建的 DisplayArea
areas.get(child.mFeature).add(area);
}
// 开始迭代构建
child.instantiateChildren(area, areaForLayer, level + 1, areas);
}
}
private DisplayArea createArea(DisplayArea<DisplayArea> parent,
DisplayArea.Tokens[] areaForLayer) {
// 当 mExisting 不为 null,前面分析的 TaskDisplayArea 和 ImeContainer 的情况,此时不需要再创建 DisplayArea 对象,直接用 mExisting
if (mExisting != null) {
// asTokens,方法定义在 DisplayArea 中默认返回 null,只有 DisplayArea.Tokens 返回本身。 而 ImeContainer 是继承 DisplayArea.Tokens 的,所以有返回值。
if (mExisting.asTokens() != null) {
// Store the WindowToken container for layers
// 只有输入法满足
fillAreaForLayers(mExisting.asTokens(), areaForLayer);
}
return mExisting;
}
// mSkipTokens 为 true,直接 return,mSkipTokens 是和 mExisting 是同一个逻辑下设置的。
if (mSkipTokens) {
return null;
}
// 定义 DisplayArea 的类型
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;
}
// DisplayArea 的类型
if (mFeature == null) {// leaf 的 mFeature null
// 构建返回的 Leaf
final DisplayArea.Tokens leaf = new DisplayArea.Tokens(parent.mWmService, type,
"Leaf:" + mMinLayer + ":" + mMaxLayer);
fillAreaForLayers(leaf, areaForLayer);// 给对应覆盖的层级都需要赋值
return leaf;
} else {// 创建 DisplayArea 对象。如果 mFeature不为 null,那么创建一个 DisplayArea 对象,注意这里构造函数 name 参数传入的是 mFeature.mName + ":" + mMinLayer + ":" + mMaxLayer,包含了 Feature 名,纵向层级范围信息。
return mFeature.mNewDisplayAreaSupplier.create(parent.mWmService, type,
mFeature.mName + ":" + mMinLayer + ":" + mMaxLayer, mFeature.mId);
}
}
该文章为第一次阅读源码所总结,后续会进行补充,学习的本质是反复。