AOSP15 WMS/AMS系统开发 - WindowManagerService relayout调用流程详解

基于 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()nullrelayout()

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_REDRAWcancelDraw = 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 != nullattachedFrame 传入父窗口的frame,供子窗口计算parentFrame
  • win.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) --- 旋转时生成对应方向LayoutParams
  • fitToDisplay --- 全屏窗口和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: 修改窗口属性后窗口行为没变

  1. adjustWindowParamsLw() 是否过滤了flag?
  2. sanitizeFlagSlippery() / sanitizeInputFeatures() 是否覆盖?
  3. win.mDisableFlags 是否被权限检查清零?
  4. attrChanges是否包含期望的bit?

场景2: 窗口显示黑屏/闪烁

  1. createSurfaceLocked()返回值是否为null?
  2. RELAYOUT_RES_FIRST_TIME设置但未调用finishDrawing()
  3. BLAST同步卡住?检查mSyncSeqIdmPrepareSyncSeqId
  4. RELAYOUT_RES_CANCEL_AND_REDRAW持续返回?

场景3: 窗口resize后内容未更新

  1. performSurfacePlacement循环超过6次?
  2. BLAST sync: 客户端是否正确处理syncSeqId
  3. fillClientWindowFramesAndConfiguration返回的帧是否正确?
  4. onClientWindowFramesChanged的seq是否乱序?

场景4: 虚拟Display上窗口被踢回默认Display

  1. DisplayWindowPolicyController.keepActivityOnWindowFlagsChanged()返回值
  2. 日志: "Activity XXX window flag changed, can't remain on display N"

场景5: 输入法弹出/收起时窗口resize异常

  1. SOFT_INPUT_ADJUST_RESIZEmLayoutNeeded = true
  2. 检查InsetsState中IME source的frame变化
  3. 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
相关推荐
阿巴斯甜1 小时前
Kotlin 高阶函数:
android
之歆2 小时前
Day03_HTML 列表、表格、表单完整指南(下)
android·javascript·html
QING6182 小时前
Kotlin之【init】—— 新手须知
android·kotlin·android jetpack
阿巴斯甜2 小时前
MMKV 和DataStore 的区别:
android
阿巴斯甜2 小时前
MVVM和MVI的区别:
android
Fate_I_C2 小时前
Android Navigation Fragment 导航实战
android·kotlin·navigation
Fate_I_C3 小时前
Adroid Data Binding数据绑定对比(findViewXX、ButterKnife)
android·kotlin·databinding
黑心的奥利奥3 小时前
WeeX跨平台框架,自定义安卓平台MarkDown文本渲染组件高度跟随内容自适应实现思路探索
android
KIHU快狐4 小时前
快狐KIHU|110寸壁挂触控一体机G+G电容屏安卓系统汽车展厅查询展示
android·python·汽车