1. WMS窗口层级树概述
在Android系统中,WindowManagerService(WMS)是负责管理所有窗口的核心服务,而窗口层级树是WMS中用于组织和管理窗口显示结构的关键数据结构。它定义了窗口在Z轴上的顺序、布局关系以及显示层级。
1.1 窗口层级树的基本概念
在Android 14中,窗口层级树是一个以RootWindowContainer为根节点的树形结构。每个节点代表一个窗口容器,可以是窗口本身,也可以是一组窗口的容器。这个树结构不仅管理了窗口的显示顺序,还负责窗口的布局和尺寸计算。
// frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.javapublicclassRootWindowContainerextendsWindowContainerimplementsDisplayManager.DisplayListener{// The root of the device window hierarchy.privatefinal WindowManagerService mWmService;// 存储所有DisplayContent的列表,每个DisplayContent对应一个显示屏privatefinal ArrayList<DisplayContent> mDisplays = new ArrayList<>();}从上述代码可以看出,RootWindowContainer作为窗口层级树的根节点,管理着所有显示屏的窗口内容。
1.2 窗口层级树的整体结构
Android 14的窗口层级树结构可以分为以下几个主要部分:
RootWindowContainer:窗口层级树的根节点,代表整个窗口管理系统 DisplayContent:代表一个显示屏,是窗口层级树中每个显示设备的容器 DisplayArea:显示屏上的一个区域,用于组织窗口的布局 Task/TaskDisplayArea:任务容器,用于管理属于同一任务的一组窗口 WindowToken:窗口令牌,一组相关窗口的容器 WindowState:代表一个具体的窗口
// 窗口层级树结构示例RootWindowContainer └── DisplayContent (主屏幕) ├── DisplayArea.Tokens (系统窗口容器) │ ├── WindowToken (StatusBar) │ ├── WindowToken (NavigationBar) │ └── WindowToken (InputMethod) ├── TaskDisplayArea (应用窗口容器) │ └── Task │ └── ActivityRecord │ └── WindowState (Activity窗口) └── WallpaperWindowToken (壁纸窗口容器)2. WMS窗口层级树核心数据结构
2.1 WindowContainer - 窗口容器基类
WindowContainer是窗口层级树中所有节点的基类,定义了窗口层级树的基本行为和属性。
// frameworks/base/services/core/java/com/android/server/wm/WindowContainer.javaclassWindowContainerextendsWindowContainer{// 父节点private WindowContainer mParent = null;// 子节点列表,按照Z轴顺序排列,最顶层的窗口容器位于列表尾部protectedfinal WindowList mChildren = new WindowList();// 窗口容器的名称private String mName;// 窗口容器的层级privateint mLayer;// 窗口容器的可见性privateboolean mVisible = true;}WindowContainer类提供了窗口层级树的基本结构,包括父子关系、Z轴顺序等核心概念。
2.2 DisplayContent - 显示屏内容
DisplayContent代表一个显示屏,是窗口层级树中每个显示设备的容器。它管理着该显示屏上的所有窗口和区域。
// frameworks/base/services/core/java/com/android/server/wm/DisplayContent.javaclassDisplayContentextendsRootDisplayAreaimplementsWindowManagerPolicy.DisplayContentInfo{// 显示屏IDprivatefinalint mDisplayId;// 显示屏的区域privatefinal Rect mDisplayFrame;// 窗口放置器privatefinal WindowSurfacePlacer mSurfacePlacer;// 输入监视器privatefinal InputMonitor mInputMonitor;// 显示策略privatefinal DisplayPolicy mDisplayPolicy;}DisplayContent负责管理单个显示屏上的所有窗口,包括布局、显示和输入处理。
2.3 WindowState - 窗口状态
WindowState代表一个具体的窗口,是窗口层级树中最底层的节点。
// frameworks/base/services/core/java/com/android/server/wm/WindowState.javaclassWindowStateextendsWindowContainerimplementsWindowManagerPolicy.WindowState{// 窗口令牌privatefinal WindowToken mToken;// 窗口属性privatefinal WindowManager.LayoutParams mAttrs;// 窗口框架privatefinal WindowFrames mWindowFrames;// 窗口是否拥有表面privateboolean mHasSurface;// 窗口是否需要布局privateboolean mLayoutNeeded;}WindowState包含了窗口的所有状态信息,包括位置、大小、可见性等。
2.4 WindowToken - 窗口令牌
WindowToken是一组相关窗口的容器,通常用于管理具有相同父窗口或用途的一组窗口。
// frameworks/base/services/core/java/com/android/server/wm/WindowToken.javaclassWindowTokenextendsWindowContainer{// 窗口令牌类型privatefinalint mType;// 父窗口privatefinal WindowContainer mParent;// 窗口列表privatefinal ArrayList<WindowState> mWindows = new ArrayList<>();}WindowToken在窗口层级树中充当中间节点,用于组织和管理一组相关的窗口。
3. WMS窗口层级树管理机制
3.1 窗口添加流程
窗口的添加是WMS的核心功能之一。当应用需要显示一个窗口时,它会通过ViewRootImpl向WMS请求添加一个窗口。
应用端请求添加窗口:
// ViewRootImpl.javapublicvoidsetView(View view, WindowManager.LayoutParams attrs, View panelParentView, int userId){// ...// 通过Binder调用WMS的addWindow方法 res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), userId, mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets, mTempControls);// ...}WMS处理添加窗口请求:
// WindowManagerService.javapublicintaddWindow(Session session, IWindow window, WindowManager.LayoutParams attrs,int requestedWidth, int requestedHeight, int viewVisibility, int flags, ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration, SurfaceControl outSurfaceControl, InsetsState outInsetsState, InsetsSourceControl[] outActiveControls, Bundle outSyncSeqIdBundle){// ...// 创建WindowState对象final WindowState win = new WindowState(this, session, window, attrs);// ...// 将窗口添加到窗口层级树中 addWindowToToken(win, attrs);// ...}将窗口添加到窗口层级树:
// WindowManagerService.javaprivatevoidaddWindowToToken(WindowState win, WindowManager.LayoutParams attrs){// ...// 获取或创建WindowToken WindowToken token = getTokenForType(attrs.type, attrs.token, attrs.packageName);// ...// 将WindowState添加到WindowToken中 token.addChild(win);// ...}
3.2 窗口布局流程
窗口布局是WMS的另一个核心功能,它决定了窗口的大小和位置。
请求布局:
// ViewRootImpl.javapublicvoidrequestLayout(){if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); }}执行布局:
// ViewRootImpl.javaprivatevoidperformTraversals(){// ...// 请求WMS重新布局窗口 relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);// ...// 执行测量 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);// ...// 执行布局if (didLayout) { performLayout(lp, mWidth, mHeight); }// ...}WMS处理布局请求:
// WindowManagerService.javapublicintrelayoutWindow(Session session, IWindow window, WindowManager.LayoutParams attrs,int requestedWidth, int requestedHeight, int viewVisibility, int flags, ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration, SurfaceControl outSurfaceControl, InsetsState outInsetsState, InsetsSourceControl[] outActiveControls, Bundle outSyncSeqIdBundle){// ...// 获取WindowState对象final WindowState win = windowForClientLocked(session, window, false);// ...// 设置请求的大小 win.setRequestedSize(requestedWidth, requestedHeight);// ...// 调整窗口参数 displayPolicy.adjustWindowParamsLw(win, attrs);// ...// 执行表面放置 mWindowPlacerLocked.performSurfacePlacement(true/* force */);// ...}执行表面放置:
// WindowSurfacePlacer.javafinalvoidperformSurfacePlacement(boolean force){// ...do { mTraversalScheduled = false;// 执行一次表面放置循环 performSurfacePlacementLoop();// ... } while (mTraversalScheduled && loopCount > 0);// ...}窗口布局计算:
// DisplayPolicy.javapublicvoidlayoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames){// ...// 获取窗口的布局参数final WindowManager.LayoutParams attrs = win.getLayoutingAttrs(displayFrames.mRotation);// ...// 计算窗口框架 mWindowLayout.computeFrames(attrs, win.getInsetsState(), displayFrames.mDisplayCutoutSafe, win.getBounds(), win.getWindowingMode(), requestedWidth, requestedHeight, win.getRequestedVisibilities(), attachedWindowFrame, win.mGlobalScale, sTmpClientFrames);// ...// 设置窗口框架 win.setFrames(sTmpClientFrames, requestedWidth, requestedHeight);}
3.3 Z轴排序机制
Z轴排序决定了窗口在垂直方向上的显示顺序,即哪个窗口位于上层,哪个窗口位于下层。
Z轴层级定义:
// WindowManagerService.javaprivatestaticfinalint FIRST_APPLICATION_WINDOW = 1;privatestaticfinalint LAST_APPLICATION_WINDOW = 99;privatestaticfinalint FIRST_SYSTEM_WINDOW = 1000;privatestaticfinalint LAST_SYSTEM_WINDOW = 1999;privatestaticfinalint FIRST_SUB_WINDOW = 2000;privatestaticfinalint LAST_SUB_WINDOW = 2999;窗口层级分配:
// WindowManagerService.javaprivatevoidassignWindowLayers(WindowState win){// ...// 根据窗口类型分配层级if (win.mAttrs.type >= FIRST_APPLICATION_WINDOW && win.mAttrs.type <= LAST_APPLICATION_WINDOW) { layer = APPLICATION_LAYER; } elseif (win.mAttrs.type >= FIRST_SYSTEM_WINDOW && win.mAttrs.type <= LAST_SYSTEM_WINDOW) { layer = SYSTEM_LAYER; } elseif (win.mAttrs.type >= FIRST_SUB_WINDOW && win.mAttrs.type <= LAST_SUB_WINDOW) { layer = SUB_WINDOW_LAYER; }// ...}窗口排序:
// WindowContainer.javaprotectedvoidsortChildren(){// 按照层级排序子窗口 Collections.sort(mChildren, (lhs, rhs) -> {if (lhs.mLayer != rhs.mLayer) {return lhs.mLayer - rhs.mLayer; }// 如果层级相同,按照添加顺序排序return lhs.mAddOrder - rhs.mAddOrder; });}
4. 布局流程详解
4.1 布局过程概述
窗口布局是WMS的核心功能之一,它决定了窗口的大小和位置。布局过程涉及多个组件的协作,包括WMS、DisplayPolicy和WindowLayout等。
布局触发:
应用程序调用 requestLayout()请求布局WMS调用 performSurfacePlacement()触发布局布局执行:
RootWindowContainer.performSurfacePlacement()开始布局过程DisplayContent.performLayout()执行显示屏布局DisplayPolicy.layoutWindowLw()计算每个窗口的布局布局应用:
WindowState.setFrames()应用计算出的布局窗口的位置和大小得到更新
4.2 布局算法
Android 14的窗口布局算法采用了多种策略,以确保窗口在不同屏幕尺寸和方向下都能正确显示。
测量阶段:
// WindowLayout.javapublicvoidcomputeFrames(WindowManager.LayoutParams attrs, InsetsState insets, Rect displayCutoutSafe, Rect bounds, int windowingMode, int requestedWidth,int requestedHeight, int requestedVisibilities, Rect attachedWindowFrame,float globalScale, ClientWindowFrames outFrames){// ...// 计算窗口的可用空间int availableWidth = displayWidth - paddingLeft - paddingRight;int availableHeight = displayHeight - paddingTop - paddingBottom;// ...// 根据布局参数计算窗口大小if (widthMeasureSpec == MeasureSpec.EXACTLY) { width = availableWidth; } elseif (widthMeasureSpec == MeasureSpec.AT_MOST) { width = Math.min(requestedWidth, availableWidth); } else { width = requestedWidth; }// ...}布局阶段:
// DisplayPolicy.javapublicvoidlayoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames){// ...// 获取窗口的布局参数final WindowManager.LayoutParams attrs = win.getLayoutingAttrs(displayFrames.mRotation);// ...// 计算窗口框架 mWindowLayout.computeFrames(attrs, win.getInsetsState(), displayFrames.mDisplayCutoutSafe, win.getBounds(), win.getWindowingMode(), requestedWidth, requestedHeight, win.getRequestedVisibilities(), attachedWindowFrame, win.mGlobalScale, sTmpClientFrames);// ...}
4.3 布局性能优化
为了提高布局性能,Android 14的WMS引入了多种优化策略:
增量布局:
只对需要布局的窗口进行计算 避免不必要的全量布局 异步布局:
将布局计算移到后台线程 减少主线程的阻塞 布局缓存:
缓存布局计算结果 避免重复计算
5. 性能优化建议
5.1 窗口层级优化
减少窗口层级:
避免过多的嵌套窗口 使用扁平化的窗口结构 合理使用窗口类型:
将窗口类型设置为合适的范围 避免使用过高的窗口类型导致排序问题 控制子窗口数量:
限制子窗口的数量 避免复杂的窗口嵌套
5.2 布局性能优化
减少布局计算:
避免频繁的布局请求 使用固定大小窗口 优化窗口更新:
批量更新窗口属性 减少不必要的重绘 使用硬件加速:
启用硬件加速 使用高效的渲染管道
5.3 常见问题及解决方案
窗口闪烁问题:
问题:窗口显示时出现闪烁 解决方案:确保窗口的正确初始化,避免多次布局 窗口布局问题:
问题:窗口大小或位置不正确 解决方案:检查布局参数,确保正确的测量和布局过程 性能问题:
问题:窗口操作卡顿 解决方案:优化布局过程,减少不必要的计算和重绘
6. 总结
Android 14的WMS窗口层级树是一个复杂而高效的数据结构,它负责管理Android系统中的所有窗口。通过深入理解其核心机制和数据结构,我们可以更好地优化应用程序的性能和用户体验。
本文详细分析了WMS窗口层级树的结构、管理机制和布局流程,并提供了性能优化建议和常见问题的解决方案。希望这些内容对您深入理解Android窗口管理系统有所帮助。
夜雨聆风