WMS 如何决定窗口大小和位置
WMS 如何决定窗口大小和位置
应用通过 LayoutParams 表达期望尺寸和位置,最终 WindowFrames 却要结合 Display、Insets、 父容器边界和窗口策略计算。布局结果是一轮协商后的系统状态,不是把应用坐标原样复制给 Surface。
LayoutParams 是请求,frame 才是结果
| 常见误解 | 正确理解 |
|---|---|
| 应用想多大窗口就多大 | 系统用 Display、Insets 和窗口策略约束应用请求,最终 frame 是协商结果 |
| 窗口位置就是 LayoutParams 里的坐标 | LayoutParams 是请求,frame 是系统采纳后的结果 |
应用声明的是“我想要多大、用什么布局参数”,系统回答的是“你实际可以放哪、占多大”。状态栏、导航栏、刘海和安全区都会压缩可用区域,这些区域信息由 Insets 表达。
数据流主链
逐步解释:
- ① 请求输入:应用在 relayout 请求里带上 LayoutParams 和测量后的宽高。
- ② 服务入口:
relayoutWindow()(WindowManagerService.java:2188)更新窗口状态,必要时创建 Surface。 - ③ 布局调度:
mWindowPlacerLocked.performSurfacePlacement(true)(同文件 2427 行)触发布局遍历。 - ④ 逐窗布局:
DisplayContent.performLayout()(DisplayContent.java:5171)遍历显示器上的窗口。 - ⑤ 策略计算:
DisplayPolicy.layoutWindowLw()(DisplayPolicy.java:1396)结合 Insets 调用computeFrames()。 - ⑥ 结果落地:
win.setFrames(...)写入 WindowFrames,应用通过 relayout 返回值拿到 frame。
四个概念一次分清
| 对象 | 是什么 | 在布局计算中的作用 |
|---|---|---|
WindowManager.LayoutParams | 应用提交的窗口参数与期望 | 装修要求 |
InsetsState | 当前各来源(状态栏、导航栏、IME)的窗口区域状态 | 障碍物清单 |
DisplayFrames | 显示器级的框架信息,供策略计算使用 | 整栋楼的验收标准 |
WindowFrames | 最终写入窗口的一组矩形 frame | 施工图 |
WindowFrames 不是单个矩形:它包含 mParentFrame、mDisplayFrame、mFrame(WindowFrames.java:41/46/51)等多个矩形,分别表达父边界、显示边界和最终内容边界。
Insets 从哪里来
Insets 由显示器级控制器管理:DisplayContent.mInsetsStateController(DisplayContent.java:736,构造于 1174 行)。窗口布局时从控制器取 InsetsState,再交给 WindowLayout.computeFrames() 计算可用区域。窗口返回给应用的 InsetsState 会驱动应用的 WindowInsets 回调。
源码证据
阅读目标:确认 relayout 触发的是“布局遍历”,不是单个窗口自算。
正文指针:WindowManagerService.java:2427;DisplayContent.java:5177 / 5210;DisplayPolicy.java:1401。
// WindowManagerService.java:2427
mWindowPlacerLocked.performSurfacePlacement(true /* force */);// DisplayContent.java:5177
private void performLayoutNoTrace(boolean initial, boolean updateInputWindows) {
...
forAllWindows(mPerformLayout, true /* traverseTopToBottom */);// DisplayPolicy.java:1401
mWindowLayout.computeFrames(attrs, win.getInsetsState(), displayFrames.mDisplayCutoutSafe,
win.getBounds(), win.getWindowingMode(), requestedWidth, requestedHeight,
win.getRequestedVisibleTypes(), win.mGlobalScale, sTmpClientFrames);这段代码证明: relayout 请求被汇聚到 WindowSurfacePlacer,DisplayContent 按容器树遍历所有窗口,DisplayPolicy 结合 Insets 计算 frames——布局是系统级的批量过程。
布局完成后
performLayoutNoTrace 末尾调用 mInputMonitor.setUpdateInputWindowsNeededLw() 与 updateInputWindowsLw(false)(DisplayContent.java:5209-5210),因为窗口位置变化后,输入 命中区域也要同步更新;后续链路见窗口输入路由。
源码入口
源码基准:android-14.0.0_r75。
以下文件默认位于 frameworks/base/services/core/java/com/android/server/wm/,表中省略该前缀:
| 文件 | 关键位置 | 作用 |
|---|---|---|
WindowManagerService.java | 2188 行 relayoutWindow();2224 行 win.setRequestedSize();2427 行 performSurfacePlacement(true) | relayout 服务入口 |
WindowSurfacePlacer.java | 114 行 performSurfacePlacement();133 行 performSurfacePlacementLoop() | 布局遍历调度 |
DisplayContent.java | 5171 行 performLayout();5177 行 performLayoutNoTrace();5209-5210 行输入窗口更新 | 逐窗布局 |
DisplayPolicy.java | 1396 行 layoutWindowLw();1401 行 mWindowLayout.computeFrames(...);1405 行 win.setFrames(...) | 策略计算 frames |
WindowFrames.java | 35 行 class WindowFrames;41 / 46 / 51 行 mParentFrame / mDisplayFrame / mFrame | 布局输出结构 |
DisplayContent.java | 736 / 1174 行 mInsetsStateController | Insets 状态管理 |