基于 AOSP 15 (Android 15) 源码分析
摘要: relayout是WMS中仅次于addWindow的高频操作,窗口每次measure/layout/draw前都会触发。本文从Framework开发视角,梳理relayout从客户端
performTraversals到服务端WMS.relayoutWindow再到WindowLayout.computeFrames帧计算的全链路。
目录
- [1. 整体架构概览](#1. 整体架构概览 "#1-%E6%95%B4%E4%BD%93%E6%9E%B6%E6%9E%84%E6%A6%82%E8%A7%88")
- [2. relayout是什么?什么时候触发?](#2. relayout是什么?什么时候触发? "#2-relayout%E6%98%AF%E4%BB%80%E4%B9%88%E4%BB%80%E4%B9%88%E6%97%B6%E5%80%99%E8%A7%A6%E5%8F%91")
- [3. 完整调用链路](#3. 完整调用链路 "#3-%E5%AE%8C%E6%95%B4%E8%B0%83%E7%94%A8%E9%93%BE%E8%B7%AF")
- [4. 客户端侧: performTraversals → relayoutWindow](#4. 客户端侧: performTraversals → relayoutWindow "#4-%E5%AE%A2%E6%88%B7%E7%AB%AF%E4%BE%A7-performtraversals--relayoutwindow")
- [4.1 relayout触发判断逻辑](#4.1 relayout触发判断逻辑 "#41-relayout%E8%A7%A6%E5%8F%91%E5%88%A4%E6%96%AD%E9%80%BB%E8%BE%91")
- [4.2 windowShouldResize判定](#4.2 windowShouldResize判定 "#42-windowshouldresize%E5%88%A4%E5%AE%9A")
- [4.3 ViewRootImpl.relayoutWindow()](#4.3 ViewRootImpl.relayoutWindow() "#43-viewrootimplrelayoutwindow")
- [4.4 relayoutAsync优化机制](#4.4 relayoutAsync优化机制 "#44-relayoutasync%E4%BC%98%E5%8C%96%E6%9C%BA%E5%88%B6")
- [5. Binder IPC: IWindowSession接口](#5. Binder IPC: IWindowSession接口 "#5-binder-ipc-iwindowsession%E6%8E%A5%E5%8F%A3")
- [6. 服务端核心: WMS.relayoutWindow()](#6. 服务端核心: WMS.relayoutWindow() "#6-%E6%9C%8D%E5%8A%A1%E7%AB%AF%E6%A0%B8%E5%BF%83-wmsrelayoutwindow")
- [6.1 阶段一: 入口校验](#6.1 阶段一: 入口校验 "#61-%E9%98%B6%E6%AE%B5%E4%B8%80-%E5%85%A5%E5%8F%A3%E6%A0%A1%E9%AA%8C-line-2310-2325")
- [6.2 阶段二: 窗口属性变更处理](#6.2 阶段二: 窗口属性变更处理 "#62-%E9%98%B6%E6%AE%B5%E4%BA%8C-%E7%AA%97%E5%8F%A3%E5%B1%9E%E6%80%A7%E5%8F%98%E6%9B%B4%E5%A4%84%E7%90%86-line-2338-2442")
- [6.3 阶段三: shouldRelayout决策与Surface管理](#6.3 阶段三: shouldRelayout决策与Surface管理 "#63-%E9%98%B6%E6%AE%B5%E4%B8%89-shouldrelayout%E5%86%B3%E7%AD%96%E4%B8%8Esurface%E7%AE%A1%E7%90%86")
- [6.4 阶段四: performSurfacePlacement → computeFrames 完整链路](#6.4 阶段四: performSurfacePlacement → computeFrames 完整链路 "#64-%E9%98%B6%E6%AE%B5%E5%9B%9B-performsurfaceplacement--computeframes-%E5%AE%8C%E6%95%B4%E9%93%BE%E8%B7%AF")
- [7. 调试方法](#7. 调试方法 "#7-%E8%B0%83%E8%AF%95%E6%96%B9%E6%B3%95")
- [8. Framework开发常见场景](#8. Framework开发常见场景 "#8-framework%E5%BC%80%E5%8F%91%E5%B8%B8%E8%A7%81%E5%9C%BA%E6%99%AF")
- [9. 完整时序图](#9. 完整时序图 "#9-%E5%AE%8C%E6%95%B4%E6%97%B6%E5%BA%8F%E5%9B%BE")
- [10. 源码文件索引](#10. 源码文件索引 "#10-%E6%BA%90%E7%A0%81%E6%96%87%E4%BB%B6%E7%B4%A2%E5%BC%95")
1. 整体架构概览
scss
┌────────────────────────────────────────────────────────────────────────┐
│ 应用进程 (App Process) │
│ │
│ 触发源 (三条路径): │
│ ├─ 首次窗口: ViewRootImpl.setView() → ViewRootImpl.requestLayout() │
│ ├─ 运行时: View.requestLayout() → mParent递归向上 │
│ │ → ViewRootImpl.requestLayout() │
│ └─ 其他: Window属性变更 / 可见性变更 / WMS回调 │
│ │ │
│ ▼ │
│ ViewRootImpl.scheduleTraversals() │
│ │ │
│ ▼ │
│ performTraversals() │
│ │ │
│ ├── relayoutWindow() ── 与WMS通信获取帧/Surface │
│ ├── performMeasure() │
│ ├── performLayout() │
│ └── performDraw() ── 将内容写入Surface的Buffer │
│ │ │
│ IWindowSession.relayout() (Binder IPC) │
└──────────────────────────────┼─────────────────────────────────────────┘
│
┌──────────────────────────────┼─────────────────────────────────────────┐
│ system_server 进程 │
│ │ │
│ ┌─────────────┐ ┌──────▼──────────┐ ┌────────────────────┐ │
│ │ Session │────▶│ WMS │────▶│ WindowState │ │
│ │(per-process)│ │ .relayoutWindow │ │ .relayoutVisible │ │
│ └─────────────┘ └──────┬──────────┘ │ Window() │ │
│ │ └────────────────────┘ │
│ ┌─────────────────────┼──────────────────────┐ │
│ │ │ │ │
│ ┌────▼──────────┐ ┌──────▼────────┐ ┌──────────▼─────────┐ │
│ │DisplayPolicy │ │WindowState │ │WindowSurfacePlacer │ │
│ │.adjustWindow │ │Animator │ │.performSurface │ │
│ │ ParamsLw() │ │.createSurface │ │ Placement() │ │
│ │ 策略调整 │ │ Locked() │ │ 强制布局 │ │
│ └───────────────┘ └───────────────┘ └────────────────────┘ │
└────────────────────────────────────────────────────────────────────────┘
核心类职责
| 类名 | 路径 | Framework开发关注点 |
|---|---|---|
ViewRootImpl |
core/.../view/ViewRootImpl.java |
relayout触发入口,处理返回的帧/Surface |
IWindowSession |
AIDL接口 | Binder IPC通道,同步/异步两种模式 |
Session |
services/.../wm/Session.java |
per-process,透传到WMS |
WindowManagerService |
services/.../wm/WindowManagerService.java |
relayout核心:属性更新、Surface管理、焦点分配 |
WindowState |
services/.../wm/WindowState.java |
窗口状态机,可见性转换、帧填充 |
WindowStateAnimator |
services/.../wm/WindowStateAnimator.java |
Surface创建和动画控制 |
WindowSurfacePlacer |
services/.../wm/WindowSurfacePlacer.java |
触发全局布局循环 |
DisplayPolicy |
services/.../wm/DisplayPolicy.java |
窗口参数策略调整,调用computeFrames的入口 |
WindowLayout |
core/.../view/WindowLayout.java |
帧计算引擎: displayFrame/parentFrame/frame |
2. relayout是什么?什么时候触发?
relayout的核心作用
relayout是客户端与WMS之间的同步协商过程:
- 客户端告诉WMS: 我想要的尺寸、属性、可见性
- WMS告诉客户端: 你实际的帧位置、Surface、配置、Insets
每次 performTraversals() 在执行 measure/layout/draw 之前,都要先通过relayout向WMS申请资源。
触发时机
| 场景 | 触发条件 | Framework开发中的典型case |
|---|---|---|
| 首次布局 | mFirst == true |
addView()后第一次traversal |
| 尺寸变化 | windowShouldResize |
旋转屏幕、分屏调整、输入法弹出 |
| 可见性变更 | viewVisibilityChanged |
setVisibility(VISIBLE/INVISIBLE/GONE) |
| 属性变更 | params != null |
修改WindowManager.LayoutParams中任一字段 |
| 强制relayout | mForceNextWindowRelayout |
配置变更、兼容模式切换 |
3. 完整调用链路
scss
[应用进程]
触发源 (三条路径):
├─ 路径1: ViewRootImpl.setView() → requestLayout() ← 首次addView
├─ 路径2: View.requestLayout() → mParent递归向上
│ → ViewRootImpl.requestLayout() ← 运行时View变更
└─ 路径3: Window属性变更 / 可见性变更 / WMS回调(resizeClient等)
│
▼
ViewRootImpl.scheduleTraversals() ← Choreographer下一帧执行
│
▼
ViewRootImpl.doTraversal()
│
▼
ViewRootImpl.performTraversals() ← View树绘制总入口
│
├── 判断 relayout 条件
│ (mFirst || windowShouldResize || visibilityChanged || params变更)
│
▼
ViewRootImpl.relayoutWindow(params, viewVisibility, insetsPending)
│
├── 计算请求尺寸 (measuredWidth * appScale)
├── 判断 relayoutAsync 快捷路径
├── IWindowSession.relayout() 或 relayoutAsync()
│
═══════════════════ Binder IPC ═══════════════════
│
[system_server]
Session.relayout()
│
▼
WMS.relayoutWindow() ← 核心方法 (~420行)
│
├── 1. 获取WindowState,序列号校验
├── 2. DisplayPolicy.adjustWindowParamsLw() 策略调整
├── 3. 属性差异计算 (flagChanges/attrChanges)
├── 4. shouldRelayout决策 → Surface创建/销毁
├── 5. performSurfacePlacement(true) 强制全局布局
│ │
│ ▼
│ WindowSurfacePlacer.performSurfacePlacement()
│ → RootWindowContainer.performSurfacePlacementNoTrace()
│ → DisplayContent.applySurfaceChangesTransaction()
│ → DisplayContent.performLayout()
│ → DisplayPolicy.layoutWindowLw()
│ → WindowLayout.computeFrames() ★ 帧计算
│
├── 6. 焦点/IME/方向/帧填充/Insets传递
│
▼
返回 result + WindowRelayoutResult
│
═══════════════════ Binder IPC ═══════════════════
│
[应用进程]
ViewRootImpl.relayoutWindow() 处理返回
│
▼
performMeasure → performLayout → performDraw
4. 客户端侧: performTraversals → relayoutWindow
4.1 relayout触发判断逻辑
java
// ViewRootImpl.performTraversals() line 3731-3771
if (mFirst || windowShouldResize || viewVisibilityChanged || params != null
|| mForceNextWindowRelayout) {
mForceNextWindowRelayout = false;
// insetsPending: 窗口给WMS提供内部insets时,先标记pending
insetsPending = computesInternalInsets
&& mWindowAttributes.providedInsets == null;
// 核心: 调用relayout
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
// 处理取消绘制
cancelDraw = (relayoutResult & RELAYOUT_RES_CANCEL_AND_REDRAW)
== RELAYOUT_RES_CANCEL_AND_REDRAW;
// 处理BLAST同步
if (mSyncSeqId > mLastSyncSeqId) {
mLastSyncSeqId = mSyncSeqId;
reportNextDraw("relayout");
mSyncBuffer = true;
}
}
4.2 windowShouldResize判定
java
// ViewRootImpl.performTraversals() (约 line 3620)
boolean windowShouldResize = layoutRequested && windowSizeMayChange
&& ((mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight())
|| (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
&& frame.width() < desiredWindowWidth && frame.width() != mWidth)
|| (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT
&& frame.height() < desiredWindowHeight && frame.height() != mHeight));
windowShouldResize |= mDragResizing && mPendingDragResizing;
Framework开发注意 : windowSizeMayChange由多因素决定------配置变更、drag resize、WRAP_CONTENT场景下desired尺寸变化等。调试时关注 mWidth != host.getMeasuredWidth() 这个条件。
4.3 ViewRootImpl.relayoutWindow()
java
// ViewRootImpl.java line 9367
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
// 1. 获取配置和测量尺寸
final WindowConfiguration winConfig = getCompatWindowConfiguration();
final int measuredWidth = mMeasuredWidth;
final int measuredHeight = mMeasuredHeight;
// 2. relayoutAsync优化判断(详见4.4节)
// 3. 计算请求尺寸(兼容性缩放)
float appScale = mAttachInfo.mApplicationScale;
final int requestedWidth = (int) (measuredWidth * appScale + 0.5f);
final int requestedHeight = (int) (measuredHeight * appScale + 0.5f);
mRelayoutSeq++;
if (relayoutAsync) {
// 异步: 只发送属性,不等待返回
mWindowSession.relayoutAsync(mWindow, params,
requestedWidth, requestedHeight, viewVisibility,
insetsPending ? RELAYOUT_INSETS_PENDING : 0,
mRelayoutSeq, mLastSyncSeqId);
} else {
// 同步: 等待WMS返回完整结果
relayoutResult = mWindowSession.relayout(mWindow, params,
requestedWidth, requestedHeight, viewVisibility,
insetsPending ? RELAYOUT_INSETS_PENDING : 0,
mRelayoutSeq, mLastSyncSeqId, mRelayoutResult);
mRelayoutRequested = true;
// 4. 处理返回的帧信息(带序列号防乱序)
onClientWindowFramesChanged(mTmpFrames);
// 5. BLAST Sync SeqId
if (mRelayoutResult.syncSeqId > 0) {
mSyncSeqId = mRelayoutResult.syncSeqId;
}
// 6. 兼容性缩放
mWinFrameInScreen.set(mTmpFrames.frame);
mInvCompatScale = 1f / mTmpFrames.compatScale;
// 7. Insets控制权
handleInsetsControlChanged(mTempInsets, mTempControls);
}
// 8. Surface相关更新
// - Transform hint: Buffer旋转与设备方向的映射
// - Surface尺寸: WindowLayout.computeSurfaceSize()
// - BlastBufferQueue: updateBlastSurfaceIfNeeded()
// - ThreadedRenderer: setSurfaceControl()
return relayoutResult;
}
onClientWindowFramesChanged --- 帧信息防乱序:
java
// ViewRootImpl.java
private void onClientWindowFramesChanged(@NonNull ClientWindowFrames inOutFrames) {
if (isIncomingSeqStale(mLastReportedFrames.seq, inOutFrames.seq)) {
// 收到旧的帧信息,忽略,使用上次报告的值
inOutFrames.setTo(mLastReportedFrames);
} else {
mLastReportedFrames.setTo(inOutFrames);
}
}
Framework开发注意: 帧信息的seq机制保证异步更新时不会用旧数据覆盖新数据。如果调试时发现窗口帧"回退",检查seq是否有问题。
4.4 relayoutAsync优化机制
为减少Binder同步调用开销,relayoutAsync在仅属性变更、帧信息不变时走oneway异步路径:
java
// ViewRootImpl.relayoutWindow() 中的判断
// 条件全部满足才考虑走异步:
// 1. 可见性未变化 (FLAG_WINDOW_VISIBILITY_CHANGED == 0)
// 2. 非StartingWindow
// 3. BLAST Sync已完成 (mSyncSeqId <= mLastSyncSeqId)
// 4. AM和WM的WindowConfiguration无差异
if (全部满足) {
// 本地预计算帧
mWindowLayout.computeFrames(attrs.forRotation(winConfig.getRotation()),
state, displayCutoutSafe, winConfig.getBounds(), ...);
// 关键: 位置和尺寸不能同时变化
// 只位置变 或 只尺寸变 → 异步
// 位置和尺寸都变 → 同步(需要BLAST sync)
relayoutAsync = !positionChanged || !sizeChanged;
} else {
relayoutAsync = false;
}
| 对比 | relayout (同步) | relayoutAsync (异步) |
|---|---|---|
| Binder | 双向同步调用 | oneway单向 |
| 返回值 | WindowRelayoutResult完整返回 |
无返回值 |
| 场景 | 首次/配置变更/Surface变更/位置+尺寸同时变 | 仅属性变更 |
| 实现 | Session.relayout() 内部 outRelayoutResult != null |
relayoutAsync() 传 null 给 relayout() |
5. Binder IPC: IWindowSession接口
java
// IWindowSession.aidl
// 同步relayout
int relayout(IWindow window, in WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewVisibility,
int flags, int seq, int lastSyncSeqId,
out @nullable WindowRelayoutResult outRelayoutResult);
// 异步relayout (oneway)
oneway void relayoutAsync(IWindow window, in WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewVisibility,
int flags, int seq, int lastSyncSeqId);
参数详解
| 参数 | 类型 | 说明 | Framework关注点 |
|---|---|---|---|
window |
IWindow |
客户端窗口Binder标识 | WMS通过它找到对应的WindowState |
attrs |
LayoutParams |
窗口属性,null表示不变 | 属性变更会触发策略调整 |
requestedWidth/Height |
int |
客户端请求尺寸 | 经过appScale缩放后的值 |
viewVisibility |
int |
VISIBLE/INVISIBLE/GONE | 决定shouldRelayout的关键输入 |
flags |
int |
RELAYOUT_INSETS_PENDING等 |
标记insets尚未就绪 |
seq |
int |
序列号 | 去重: 旧seq被忽略直接return 0 |
lastSyncSeqId |
int |
客户端已完成的BLAST同步ID | 用于BLAST cancelAndRedraw判断 |
6. 服务端核心: WMS.relayoutWindow()
源码 :
WindowManagerService.java整体分为4个阶段,按执行顺序排列:
6.1 阶段一: 入口校验
java
synchronized (mGlobalLock) {
// ① 获取WindowState,找不到直接返回
final WindowState win = windowForClientLocked(session, client, false);
if (win == null) return 0;
// ② 序列号去重: 旧请求直接忽略
if (win.mRelayoutSeq < seq) {
win.mRelayoutSeq = seq;
} else if (win.mRelayoutSeq > seq) {
return 0;
}
// ③ BLAST同步取消: WMS正在等sync buffer,但客户端要非sync绘制
if (win.cancelAndRedraw() && win.mPrepareSyncSeqId <= lastSyncSeqId) {
result |= RELAYOUT_RES_CANCEL_AND_REDRAW;
}
Framework注意: 客户端收到
RELAYOUT_RES_CANCEL_AND_REDRAW→cancelDraw = true,跳过本帧绘制,等下一帧重试。
6.2 阶段二: 窗口属性变更处理
当 attrs != null 时处理窗口属性变更:
java
if (attrs != null) {
// ① DisplayPolicy策略调整
displayPolicy.adjustWindowParamsLw(win, attrs);
// ② 安全过滤
attrs.flags = sanitizeFlagSlippery(attrs.flags, win.getName(), uid, pid);
attrs.inputFeatures = sanitizeInputFeatures(attrs.inputFeatures, ...);
// ③ 权限检查: 状态栏禁用标志需要STATUS_BAR权限
int disableFlags = (attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility)
& DISABLE_MASK;
if (disableFlags != 0 && !hasStatusBarPermission(pid, uid)) {
disableFlags = 0;
}
win.mDisableFlags = disableFlags;
// ④ 不可变属性校验: 窗口类型不可变更
if (win.mAttrs.type != attrs.type) {
throw new IllegalArgumentException(
"Window type can not be changed after the window is added.");
}
// ⑤ 计算属性差异 (XOR)
flagChanges = win.mAttrs.flags ^ attrs.flags;
privateFlagChanges = win.mAttrs.privateFlags ^ attrs.privateFlags;
attrChanges = win.mAttrs.copyFrom(attrs);
// ⑥ 布局变更标记
if ((attrChanges & LAYOUT_CHANGED) != 0
|| (attrChanges & SYSTEM_UI_VISIBILITY_CHANGED) != 0) {
win.mLayoutNeeded = true;
}
// ⑦ 锁屏flag变更通知ActivityRecord
if (win.mActivityRecord != null
&& ((flagChanges & FLAG_SHOW_WHEN_LOCKED) != 0
|| (flagChanges & FLAG_DISMISS_KEYGUARD) != 0)) {
win.mActivityRecord.checkKeyguardFlagsChanged();
}
// ⑧ TrustedOverlay更新(影响Input系统的触摸信任链)
if (wasTrustedOverlay != win.isWindowTrustedOverlay()) {
win.updateTrustedOverlay();
}
// ⑨ DWPC检查: 虚拟Display的窗口flag不满足策略 → 重定位到默认Display
if (displayContent.mDwpcHelper.hasController()
&& win.mActivityRecord != null
&& (!win.mRelayoutCalled || flagChanges != 0 || privateFlagChanges != 0)) {
if (!displayContent.mDwpcHelper.keepActivityOnWindowFlagsChanged(...)) {
mH.sendMessage(mH.obtainMessage(H.REPARENT_TASK_TO_DEFAULT_DISPLAY, ...));
return 0;
}
}
}
Framework注意:
attrChanges通过LayoutParams.copyFrom()计算,用XOR比较每个字段。调试"窗口行为突然变了"时先看flagChanges。
6.3 阶段三: shouldRelayout决策与Surface管理
shouldRelayout决策:
java
win.mRelayoutCalled = true;
win.mInRelayout = true;
win.setViewVisibility(viewVisibility);
final boolean shouldRelayout = viewVisibility == View.VISIBLE
&& (win.mActivityRecord == null
|| win.mAttrs.type == TYPE_APPLICATION_STARTING
|| win.mActivityRecord.isClientVisible());
决策树与分支处理:
scss
shouldRelayout?
│
├── true (应显示)
│ │
│ ├── createSurfaceControl() ← 创建Surface
│ │ └── WindowStateAnimator.createSurfaceLocked()
│ │ → SurfaceControl.Builder.setBLASTLayer().build()
│ │
│ └── (performSurfacePlacement在两个分支之后统一执行)
│
└── false (不应显示)
│
└── tryStartExitingAnimation() ← 退出动画
│
├── 有匹配动画 → mAnimatingExit = true (等动画结束再销毁)
└── 无匹配 → win.destroySurface() ← 直接销毁Surface
注意:
performSurfacePlacement(true)在shouldRelayout的两个分支之后统一执行(line 2541),不在分支内部。
Surface创建细节 --- WindowStateAnimator.createSurfaceLocked():
java
// WindowStateAnimator.java (约 line 294)
SurfaceControl createSurfaceLocked() {
if (mSurfaceControl != null) {
return mSurfaceControl; // 已存在直接返回
}
w.setHasSurface(false);
resetDrawState(); // → DRAW_PENDING
int flags = SurfaceControl.HIDDEN;
mSurfaceControl = mWin.makeSurface()
.setParent(mWin.mSurfaceControl) // 父Surface (WindowContainer的SC)
.setName(mTitle) // 窗口名 (调试可见)
.setFormat(format)
.setFlags(flags)
.setMetadata(METADATA_WINDOW_TYPE, attrs.type)
.setMetadata(METADATA_OWNER_UID, mSession.mUid)
.setMetadata(METADATA_OWNER_PID, mSession.mPid)
.setCallsite("WindowSurfaceController")
.setBLASTLayer() // BLAST BufferQueue模式
.build();
w.setHasSurface(true);
w.mInputWindowHandle.forceChange(); // 通知Input系统
mLastHidden = true; // 新建的Surface默认隐藏
return mSurfaceControl;
}
6.4 阶段四: performSurfacePlacement → computeFrames 完整链路
relayout在Surface创建/处理后,强制执行一次全局布局 (line 2541),这条链路最终调用 WindowLayout.computeFrames() 为每个窗口计算帧:
java
// WMS.java
mWindowPlacerLocked.performSurfacePlacement(true /* force */);
总览调用链:
scss
WMS.relayoutWindow() line 2541
│
▼
① WindowSurfacePlacer.performSurfacePlacement(force) // line 118
▼
② WindowSurfacePlacer.performSurfacePlacementLoop() // line 133
▼
③ RootWindowContainer.performSurfacePlacement() // line 755
▼
④ RootWindowContainer.performSurfacePlacementNoTrace() // line 766
▼
⑤ RootWindowContainer.applySurfaceChangesTransaction() // line 792
▼
⑥ DisplayContent.applySurfaceChangesTransaction() // line 5036
▼
⑦ DisplayContent.performLayout() // line 5153
▼
⑧ DisplayContent.performLayoutNoTrace() // line 5162
│
├── 第一轮: forAllWindows(mPerformLayout) 根窗口 // line 5182
│ └── DisplayPolicy.layoutWindowLw(w, null) // line 945
│ └── WindowLayout.computeFrames() ★
│
└── 第二轮: forAllWindows(mPerformLayoutAttached) 子窗口 // line 5187
└── DisplayPolicy.layoutWindowLw(w, parent) // line 984
└── WindowLayout.computeFrames() ★
① WindowSurfacePlacer.performSurfacePlacement(force)
java
// WindowSurfacePlacer.java line 114-131
final void performSurfacePlacement(boolean force) {
// force=true(来自relayout)时忽略延迟计数,直接执行
if (mDeferDepth > 0 && !force) {
mDeferredRequests++;
return;
}
int loopCount = 6; // 最多循环6次
do {
mTraversalScheduled = false;
performSurfacePlacementLoop(); // ← 核心
mService.mAnimationHandler.removeCallbacks(mPerformSurfacePlacement);
loopCount--;
} while (mTraversalScheduled && loopCount > 0);
// 循环结束: 壁纸action标记清除
mService.mRoot.mWallpaperActionPending = false;
}
Framework开发关注:
loopCount = 6--- 如果一次布局循环后又有新的布局请求(mTraversalScheduled重新被设置),会继续循环,最多6次- 超过6次日志:
"Performed 6 layouts in a row. Skipping"force=true来自relayout,确保不被deferLayout延迟
② WindowSurfacePlacer.performSurfacePlacementLoop()
java
// WindowSurfacePlacer.java line 133-179
private void performSurfacePlacementLoop() {
// 递归保护: 如果已在布局中,直接返回
if (mInLayout) {
Slog.w(TAG, "performLayoutAndPlaceSurfacesLocked called while in layout.");
return;
}
// 等待默认Display的配置就绪
final DisplayContent defaultDisplay = mService.getDefaultDisplayContentLocked();
if (defaultDisplay.mWaitingForConfig) {
return; // 配置未就绪,等下次
}
if (!mService.mDisplayReady) {
return; // Display未初始化
}
mInLayout = true;
// 清理因内存不足而强制移除的窗口
if (!mService.mForceRemoves.isEmpty()) {
while (!mService.mForceRemoves.isEmpty()) {
final WindowState ws = mService.mForceRemoves.remove(0);
Slog.i(TAG, "Force removing: " + ws);
ws.removeImmediately();
}
// 等待250ms让状态稳定
synchronized (tmp) { tmp.wait(250); }
}
try {
mService.mRoot.performSurfacePlacement(); // ← 进入RootWindowContainer
mInLayout = false;
// ...
} catch ...
}
Framework开发关注:
mInLayout递归保护 --- 如果布局过程中又触发布局会被拒绝mForceRemoves--- 内存压力时窗口被强制移除的场景
③ RootWindowContainer.performSurfacePlacement()
java
// RootWindowContainer.java
void performSurfacePlacement() {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performSurfacePlacement");
try {
performSurfacePlacementNoTrace();
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
简单包装,加systrace tag,perfetto中可见 performSurfacePlacement。
④ RootWindowContainer.performSurfacePlacementNoTrace()
java
// RootWindowContainer.java
void performSurfacePlacementNoTrace() {
// ① 焦点预分配: 如果之前有焦点变更标记
if (mWmService.mFocusMayChange) {
mWmService.mFocusMayChange = false;
mWmService.updateFocusedWindowLocked(
UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
}
// ② 清理临时状态
mDisplayBrightnessOverrides.clear();
mUserActivityTimeout = -1;
// ③ 事务序列号递增 --- 每次布局唯一标识
mWmService.mTransactionSequence++;
// ④ 核心: 应用Surface变更事务
try {
applySurfaceChangesTransaction(); // ← 进入⑤
} catch (RuntimeException e) {
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
}
// ⑤ 后续处理 (布局之后)
handleResizingWindows(); // 通知客户端窗口resize
clearFrameChangingWindows();
mWmService.mAtmService.getLifecycleManager().dispatchPendingTransactions();
mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
mWmService.mSyncEngine.onSurfacePlacement(); // BLAST同步引擎
checkAppTransitionReady(surfacePlacer); // App转场动画就绪检查
// ...
}
Framework开发关注:
mTransactionSequence++--- 调试时可通过dumpsys看到当前transaction序列号applySurfaceChangesTransaction()是帧计算的真正入口- 后续的
handleResizingWindows()会把计算好的帧通过Binder返回给客户端
⑤ RootWindowContainer.applySurfaceChangesTransaction()
java
// RootWindowContainer.java(在performSurfacePlacementNoTrace中被调用)
applySurfaceChangesTransaction();
// 实际实现: 遍历所有DisplayContent
// RootWindowContainer.java
void applySurfaceChangesTransaction() {
// 遍历每个Display
for (int displayNdx = 0; displayNdx < mChildren.size(); ++displayNdx) {
final DisplayContent dc = mChildren.get(displayNdx);
dc.applySurfaceChangesTransaction(); // ← 进入⑥
}
}
多Display架构: 每个DisplayContent独立执行自己的布局。
⑥ DisplayContent.applySurfaceChangesTransaction()
java
// DisplayContent.java
void applySurfaceChangesTransaction() {
final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
beginHoldScreenUpdate();
mTmpUpdateAllDrawn.clear();
// ① 壁纸窗口调整
if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
mWallpaperController.adjustWallpaperWindows();
}
// ② 配置变更 → 重新计算方向
if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_CONFIG) != 0) {
if (updateOrientation()) { // 屏幕旋转
setLayoutNeeded(); // 标记需要重新布局
sendNewConfiguration();
}
}
// ③ 布局重做标记
if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
setLayoutNeeded(); // 标记需要重新布局
}
// ④ ★ 执行布局 --- 核心入口
performLayout(true /* initial */, false /* updateInputWindows */); // ← 进入⑦
pendingLayoutChanges = 0;
// ⑤ 布局后策略
mDisplayPolicy.beginPostLayoutPolicyLw();
forAllWindows(mApplyPostLayoutPolicy, true /* traverseTopToBottom */);
mDisplayPolicy.finishPostLayoutPolicyLw();
// ⑥ Insets分发 (布局后)
mInsetsStateController.onPostLayout();
// ⑦ Surface显示/隐藏
forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
// ⑧ IME显示检查
mInsetsStateController.getImeSourceProvider().checkAndStartShowImePostLayout();
// ⑨ Display属性更新 (刷新率/HDR等)
mWmService.mDisplayManagerInternal.setDisplayProperties(...);
// ⑩ 壁纸可见性通知
updateRecording();
// ...
}
Framework开发关注:
pendingLayoutChanges的三个FINISH_LAYOUT_REDO_*标志控制是否重新布局setLayoutNeeded()是触发performLayout的前提条件,没有这个标记布局会跳过- 布局顺序: performLayout → postLayoutPolicy → insets分发 → Surface变更
⑦ DisplayContent.performLayout()
java
// DisplayContent.java
void performLayout(boolean initial, boolean updateInputWindows) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performLayout");
try {
performLayoutNoTrace(initial, updateInputWindows);
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
⑧ DisplayContent.performLayoutNoTrace() --- 两轮布局
java
// DisplayContent.java line 5162-5194
private void performLayoutNoTrace(boolean initial, boolean updateInputWindows) {
// ★ 前置检查: 没有layoutNeeded标记直接返回
if (!isLayoutNeeded()) {
return;
}
clearLayoutNeeded();
if (DEBUG_LAYOUT) {
Slog.v(TAG, "-------------------------------------");
Slog.v(TAG, "performLayout: dw=" + mDisplayInfo.logicalWidth
+ " dh=" + mDisplayInfo.logicalHeight);
}
int seq = mLayoutSeq + 1;
if (seq < 0) seq = 0;
mLayoutSeq = seq; // 布局序列号递增
mTmpInitial = initial;
// ★★★ 第一轮: 根窗口布局 (非attached窗口) ★★★
forAllWindows(mPerformLayout, true /* traverseTopToBottom */);
// ★★★ 第二轮: 子窗口布局 (attached窗口) ★★★
// 子窗口依赖父窗口的帧,所以必须先完成根窗口布局
forAllWindows(mPerformLayoutAttached, true /* traverseTopToBottom */);
// 通知InputDispatcher窗口帧可能变化
mInputMonitor.setUpdateInputWindowsNeededLw();
if (updateInputWindows) {
mInputMonitor.updateInputWindowsLw(false /*force*/);
}
}
第一轮回调 mPerformLayout --- 根窗口 (line 912-965):
java
// DisplayContent.java line 912
private final Consumer<WindowState> mPerformLayout = w -> {
// 跳过子窗口 (子窗口在第二轮处理)
if (w.mLayoutAttached) {
return;
}
// 跳过条件: 窗口已gone且不需要重新布局
final boolean gone = w.isGoneForLayout();
if (!gone || !w.mHaveFrame || w.mLayoutNeeded) {
if (mTmpInitial) {
w.resetContentChanged();
}
w.mSurfacePlacementNeeded = true;
w.mLayoutNeeded = false;
// ★ 调用DisplayPolicy执行布局
getDisplayPolicy().layoutWindowLw(w, null /* attached */, mDisplayFrames);
w.mLayoutSeq = mLayoutSeq;
// 首次布局: 初始化lastFrames避免不必要的resize
final boolean firstLayout = !w.isLaidOut();
if (firstLayout) {
if (!w.getFrame().isEmpty()) {
w.updateLastFrames();
mWmService.mFrameChangingWindows.remove(w);
}
w.updateSurfacePositionNonOrganized();
w.onResizeHandled();
}
}
};
第二轮回调 mPerformLayoutAttached --- 子窗口
java
// DisplayContent.java line 967
private final Consumer<WindowState> mPerformLayoutAttached = w -> {
// 跳过非子窗口
if (!w.mLayoutAttached) {
return;
}
// 条件: 可见 且 已调用relayout,或者没有帧,或者需要重新布局
if ((w.mViewVisibility != GONE && w.mRelayoutCalled) || !w.mHaveFrame
|| w.mLayoutNeeded) {
if (mTmpInitial) {
w.resetContentChanged();
}
w.mSurfacePlacementNeeded = true;
w.mLayoutNeeded = false;
// ★ 和第一轮相同,但传入父窗口引用
getDisplayPolicy().layoutWindowLw(w, w.getParentWindow(), mDisplayFrames);
w.mLayoutSeq = mLayoutSeq;
}
};
Framework开发关注:
- 两轮布局 : 根窗口先布局,子窗口后布局。子窗口的
attachedFrame来自父窗口的getFrame(),所以必须等第一轮完成isLayoutNeeded()是关键门槛 ---setLayoutNeeded()在多处被调用: relayout属性变更、配置变更、方向变更等mLayoutSeq用于判断窗口是否在最近一次布局中被处理过 (w.mLayoutSeq == mLayoutSeq)isGoneForLayout()包含多个条件:mViewVisibility == GONE || !mRelayoutCalled || !mToken.isVisible()等
⑨ DisplayPolicy.layoutWindowLw --- computeFrames的直接调用者
java
// DisplayPolicy.java line 1385-1408
public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) {
// ① 跳过标记
if (win.skipLayout()) {
return;
}
// ② 获取窗口所在Display的帧信息
displayFrames = win.getDisplayFrames(displayFrames);
// ③ 根据屏幕旋转生成对应方向的LayoutParams
// 如果当前rotation和窗口不匹配,会生成旋转后的副本
final WindowManager.LayoutParams attrs = win.mAttrs.forRotation(displayFrames.mRotation);
sTmpClientFrames.attachedFrame = attached != null ? attached.getFrame() : null;
// ④ trustedSize: attrs因旋转而变化时,不能用客户端请求的尺寸
// 因为客户端还没来得及为新rotation发送尺寸
final boolean trustedSize = attrs == win.mAttrs;
final int requestedWidth = trustedSize ? win.mRequestedWidth : UNSPECIFIED_LENGTH;
final int requestedHeight = trustedSize ? win.mRequestedHeight : UNSPECIFIED_LENGTH;
// ⑤ ★ 核心: 调用computeFrames计算帧
mWindowLayout.computeFrames(attrs, win.getInsetsState(), displayFrames.mDisplayCutoutSafe,
win.getBounds(), win.getWindowingMode(), requestedWidth, requestedHeight,
win.getRequestedVisibleTypes(), win.mGlobalScale, sTmpClientFrames);
// ⑥ 应用计算结果到WindowState
win.setFrames(sTmpClientFrames, win.mRequestedWidth, win.mRequestedHeight);
}
Framework开发关注:
attrs.forRotation(rotation)--- 屏幕旋转时,窗口的LayoutParams可能因方向不同而变化。返回自身引用表示无需旋转,返回新对象表示需要旋转trustedSize--- 如果发生了旋转(attrs对象变了),requestedWidth/Height被设为UNSPECIFIED_LENGTH(-1),computeFrames会改用LayoutParams中的值attached != null时attachedFrame传入父窗口的frame,供子窗口计算parentFramewin.setFrames()将计算好的displayFrame/parentFrame/frame写入WindowState
⑩ WindowLayout.computeFrames --- 帧计算核心
java
// WindowLayout.java line 65
public void computeFrames(WindowManager.LayoutParams attrs, InsetsState state,
Rect displayCutoutSafe, Rect windowBounds, @WindowingMode int windowingMode,
int requestedWidth, int requestedHeight, @InsetsType int requestedVisibleTypes,
float compatScale, ClientWindowFrames frames) {
final int type = attrs.type;
final int fl = attrs.flags;
final int pfl = attrs.privateFlags;
final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) == FLAG_LAYOUT_IN_SCREEN;
final Rect attachedWindowFrame = frames.attachedFrame;
final Rect outDisplayFrame = frames.displayFrame;
final Rect outParentFrame = frames.parentFrame;
final Rect outFrame = frames.frame;
// ━━━━━━ Step 1: 计算 displayFrame (从windowBounds中扣除Insets) ━━━━━━
final Insets insets = state.calculateInsets(windowBounds, attrs.getFitInsetsTypes(),
attrs.isFitInsetsIgnoringVisibility());
final @WindowInsets.Side.InsetsSide int sides = attrs.getFitInsetsSides();
final int left = (sides & WindowInsets.Side.LEFT) != 0 ? insets.left : 0;
final int top = (sides & WindowInsets.Side.TOP) != 0 ? insets.top : 0;
final int right = (sides & WindowInsets.Side.RIGHT) != 0 ? insets.right : 0;
final int bottom = (sides & WindowInsets.Side.BOTTOM) != 0 ? insets.bottom : 0;
outDisplayFrame.set(windowBounds.left + left, windowBounds.top + top,
windowBounds.right - right, windowBounds.bottom - bottom);
// ━━━━━━ Step 2: 计算 parentFrame ━━━━━━
if (attachedWindowFrame == null) {
// 非子窗口: parentFrame = displayFrame (可能被IME裁剪)
outParentFrame.set(outDisplayFrame);
if ((pfl & PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME) != 0) {
final InsetsSource source = state.peekSource(ID_IME);
if (source != null) {
outParentFrame.inset(source.calculateInsets(outParentFrame, false));
}
}
} else {
// 子窗口: layoutInScreen=false时取attachedWindowFrame,否则取displayFrame
outParentFrame.set(!layoutInScreen ? attachedWindowFrame : outDisplayFrame);
}
// ━━━━━━ Step 3: 刘海屏裁剪 ━━━━━━
final int cutoutMode = attrs.layoutInDisplayCutoutMode;
final DisplayCutout cutout = state.getDisplayCutout();
if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS && !cutout.isEmpty()) {
// SHORT_EDGES: 短边方向放开限制
// DEFAULT: 遵守displayCutoutSafe
// 子窗口和floating IN_SCREEN窗口不需要额外裁剪
if (!attachedInParent && !floatingInScreenWindow) {
intersectOrClamp(outParentFrame, displayCutoutSafeExceptMaybeBars);
}
intersectOrClamp(outDisplayFrame, displayCutoutSafeExceptMaybeBars);
}
// FLAG_LAYOUT_NO_LIMITS: 全屏窗口可以超出display边界
if (noLimits && type != TYPE_SYSTEM_ERROR && !inMultiWindowMode) {
outDisplayFrame.set(MIN_X, MIN_Y, MAX_X, MAX_Y);
}
// ━━━━━━ Step 4: 计算窗口尺寸 w/h ━━━━━━
final int pw = outParentFrame.width();
final int ph = outParentFrame.height();
int rw = requestedWidth;
int rh = requestedHeight;
// 未测量过或extendedByCutout时,用LayoutParams中的值
if (rw == UNSPECIFIED_LENGTH || extendedByCutout) {
rw = attrs.width >= 0 ? attrs.width : pw;
}
if (rh == UNSPECIFIED_LENGTH || extendedByCutout) {
rh = attrs.height >= 0 ? attrs.height : ph;
}
if ((attrs.flags & FLAG_SCALED) != 0) {
// SCALED模式: 用LayoutParams指定值
w = attrs.width < 0 ? pw : (hasCompatScale ? (int)(attrs.width * compatScale + .5f) : attrs.width);
h = attrs.height < 0 ? ph : (hasCompatScale ? (int)(attrs.height * compatScale + .5f) : attrs.height);
} else {
// 正常模式: MATCH_PARENT→parentFrame尺寸,否则→requested
w = attrs.width == MATCH_PARENT ? pw : (hasCompatScale ? (int)(rw * compatScale + .5f) : rw);
h = attrs.height == MATCH_PARENT ? ph : (hasCompatScale ? (int)(rh * compatScale + .5f) : rh);
}
// 多窗口模式: 尺寸不超过parentFrame
if (inMultiWindowMode && !hasLayoutChildFlag) {
w = Math.min(w, pw);
h = Math.min(h, ph);
}
// ━━━━━━ Step 5: Gravity应用 → 最终frame ━━━━━━
// 根据gravity将(w,h)放置到parentFrame中
Gravity.apply(attrs.gravity, w, h, outParentFrame,
(int)(attrs.x + attrs.horizontalMargin * pw),
(int)(attrs.y + attrs.verticalMargin * ph), outFrame);
// 确保窗口不超出displayFrame
if (fitToDisplay) {
Gravity.applyDisplay(attrs.gravity, outDisplayFrame, outFrame);
}
}
Framework开发关注 --- 三个核心帧的含义:
帧 含义 计算方式 displayFrame 扣除Insets(状态栏/导航栏)后的可用区域 windowBounds - fitInsets parentFrame 窗口尺寸参考基准(子窗口=父窗口frame) 根窗口=displayFrame,子窗口=attachedFrame frame 最终窗口位置和尺寸 Gravity.apply(parentFrame, w, h)
- 布局循环最多6次 。超过后日志
"Performed 6 layouts in a row. Skipping"- 两轮布局: 根窗口先,子窗口后。子窗口帧依赖父窗口
attrs.forRotation(rotation)--- 旋转时生成对应方向LayoutParamsfitToDisplay--- 全屏窗口和Popup/Dialog会被displayFrame约束,SurfaceView不会compatScale--- 兼容模式下帧尺寸会被缩放
7. 调试方法
ProtoLog开关
bash
adb shell dumpsys activity logging enable WM_DEBUG_FOCUS
adb shell dumpsys activity logging enable WM_DEBUG_ANIM
adb shell dumpsys activity logging enable WM_DEBUG_SCREEN_ON
adb shell dumpsys activity logging enable WM_SHOW_TRANSACTIONS
adb shell dumpsys activity logging enable WM_SHOW_SURFACE_ALLOC
adb shell dumpsys activity logging enable WM_DEBUG_LAYOUT
adb shell dumpsys activity logging enable WM_DEBUG_VISIBILITY
adb shell dumpsys activity logging enable WM_DEBUG_SYNC_ENGINE
dumpsys查看
bash
adb dumpsys window windows | grep -A 30 "Window #"
adb dumpsys window frames
adb dumpsys window windows | grep "mCurrentFocus"
adb dumpsys window windows | grep "mInputMethodTarget"
adb dumpsys window insets
adb dumpsys SurfaceFlinger --list
systrace/perfetto
bash
atrace -b 32768 wm view am gfx input sched freq --async_time > trace.bin
# perfetto.dev中查看: relayoutWindow / createSurfaceControl / performSurfacePlacement / wmUpdateFocus
8. Framework开发常见场景
场景1: 修改窗口属性后窗口行为没变
adjustWindowParamsLw()是否过滤了flag?sanitizeFlagSlippery()/sanitizeInputFeatures()是否覆盖?win.mDisableFlags是否被权限检查清零?attrChanges是否包含期望的bit?
场景2: 窗口显示黑屏/闪烁
createSurfaceLocked()返回值是否为null?RELAYOUT_RES_FIRST_TIME设置但未调用finishDrawing()?- BLAST同步卡住?检查
mSyncSeqId和mPrepareSyncSeqId RELAYOUT_RES_CANCEL_AND_REDRAW持续返回?
场景3: 窗口resize后内容未更新
- performSurfacePlacement循环超过6次?
- BLAST sync: 客户端是否正确处理
syncSeqId? fillClientWindowFramesAndConfiguration返回的帧是否正确?onClientWindowFramesChanged的seq是否乱序?
场景4: 虚拟Display上窗口被踢回默认Display
DisplayWindowPolicyController.keepActivityOnWindowFlagsChanged()返回值- 日志:
"Activity XXX window flag changed, can't remain on display N"
场景5: 输入法弹出/收起时窗口resize异常
SOFT_INPUT_ADJUST_RESIZE→mLayoutNeeded = true- 检查
InsetsState中IME source的frame变化 computeImeTarget()是否正确识别IME目标
9. 完整时序图
css
App Process system_server
────────── ─────────────
│
│ [触发源] 三条路径:
│ ① setView() → requestLayout() (首次addView)
│ ② View.requestLayout() → mParent递归 → ViewRootImpl.requestLayout()
│ ③ Window属性变更 / 可见性变更 / WMS回调
▼
ViewRootImpl.scheduleTraversals()
│
▼
performTraversals()
│ 判断 relayout 条件
▼
relayoutWindow()
│ 计算尺寸, 判断async
│
│ IWindowSession.relayout()
│──────────────────────────────▶│
│ │
│ Session.relayout()
│ │
│ ▼
│ WMS.relayoutWindow()
│ │
│ [阶段1] 序列号校验 + cancelAndRedraw
│ │
│ [阶段2] attrs? → adjustWindowParamsLw
│ → sanitizeFlags → attrChanges
│ → checkKeyguard → DWPC
│ │
│ [阶段3] shouldRelayout?
│ ┌──Yes────────┤────No──────────┐
│ createSurfaceControl │ tryStartExiting
│ createSurfaceLocked()│ Animation()
│ → BLAST Surface │ 或 destroySurface
│ │ │ │
│ ▼◀─────────────┘ │
│ │ │
│ [阶段4] performSurfacePlacement(true)
│ │
│ WindowSurfacePlacer → RootWindowContainer
│ → DisplayContent.applySurfaceChangesTransaction
│ → DisplayContent.performLayout()
│ → DisplayPolicy.layoutWindowLw()
│ → WindowLayout.computeFrames() ★
│ │
│ 后续: 焦点/IME/方向/帧填充/Insets
│ │
│ result + WindowRelayoutResult │
│◀────────────────────────────────────────────────│
│
▼
处理返回结果 → performMeasure → performLayout → performDraw
│
│ finishDrawing(buffer, syncSeqId)
│──────────────────────────────▶│ WMS完成BLAST同步
10. 源码文件索引
| 文件 | 路径 | 关键方法/行号 |
|---|---|---|
| ViewRootImpl.java | core/java/android/view/ |
performTraversals() ~3454, relayoutWindow() ~9367 |
| IWindowSession.aidl | core/java/android/view/ |
relayout() ~94, relayoutAsync() ~110 |
| Session.java | services/.../wm/ |
relayout() ~278, relayoutAsync() ~289 |
| WindowManagerService.java | services/.../wm/ |
relayoutWindow() ~2280, createSurfaceControl() ~2760, tryStartExitingAnimation() ~2712 |
| WindowState.java | services/.../wm/ |
relayoutVisibleWindow() ~4979, prepareWindowToDisplayDuringRelayout() ~2851, fillClientWindowFramesAndConfiguration() ~3694, cancelAndRedraw() ~6175 |
| WindowStateAnimator.java | services/.../wm/ |
createSurfaceLocked() ~294, applyEnterAnimationLocked() ~548 |
| WindowSurfacePlacer.java | services/.../wm/ |
performSurfacePlacement() ~114, performSurfacePlacementLoop() ~133 |
| RootWindowContainer.java | services/.../wm/ |
performSurfacePlacementNoTrace() ~764, applySurfaceChangesTransaction() ~792 |
| DisplayPolicy.java | services/.../wm/ |
adjustWindowParamsLw() ~950, layoutWindowLw() ~1385 |
| DisplayContent.java | services/.../wm/ |
applySurfaceChangesTransaction() ~5036, performLayout() ~5153, performLayoutNoTrace() ~5162, mPerformLayout ~912 |
| WindowLayout.java | core/java/android/view/ |
computeFrames() ~65, computeSurfaceSize() ~314 |
| WindowRelayoutResult.java | core/java/android/view/ |
完整类 ~1-140 |
| ClientWindowFrames.java | core/java/android/window/ |
完整类 ~1-156 |
| WindowManagerGlobal.java | core/java/android/view/ |
RELAYOUT_RES_* 常量 ~80-113 |
| InputMonitor.java | services/.../wm/ |
updateInputWindowsLw() ~328 |
| UnknownAppVisibilityController.java | services/.../wm/ |
notifyRelayouted() ~141 |