编程进阶网 编程进阶网
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • Android提升进阶

    • 库的解读

    • 专栏博客

      • 系统启动Zygote
      • Binder通信原理
      • Handler消息机制
      • Activity启动原理
      • 四大组件原理分析
      • AMS与组件管理
      • View绑制与渲染
      • 事件分发机制
      • Surface渲染原理
      • 自定义View设计
      • WMS窗口管理
        • PMS与APK安装
        • 虚拟机与类加载
        • 内存管理与GC
        • 线程与并发编程
        • 性能优化与监控
        • 序列化与数据存储
        • 组件化与路由设计
        • 插件化与热修复
        • NDK开发实践
        • WebView核心设计
        • ADB常见使用操作
      • 智能硬件

    • iOS开发和进阶

    • Web开发和进阶

    • Linux应用开发

    • Apps
    • Android提升进阶
    • 专栏博客
    杨充
    2026-04-14
    目录

    WMS窗口管理

    # 11.WMS窗口管理

    # 目录介绍

    • 01.为什么需要窗口管理系统
      • 1.1 多窗口的核心挑战
      • 1.2 WMS的核心职责
      • 1.3 WMS在系统中的位置
    • 02.WMS的整体架构设计
      • 2.1 核心类关系
      • 2.2 WMS的线程模型
      • 2.3 Session机制
    • 03.WMS的启动与初始化
      • 3.1 WMS的创建时机
      • 3.2 WMS构造过程
      • 3.3 Display初始化
    • 04.Window的类型与层级体系
      • 4.1 窗口类型分类
      • 4.2 窗口层级排序
      • 4.3 子窗口的层级计算
    • 05.WindowState的核心数据结构
      • 5.1 WindowState详解
      • 5.2 WindowToken的作用
      • 5.3 DisplayContent的结构
    • 06.addWindow的完整流程
      • 6.1 应用端发起addWindow
      • 6.2 WMS.addWindow核心逻辑
      • 6.3 addWindow的安全检查
    • 07.removeWindow的流程
      • 7.1 窗口移除的触发方式
      • 7.2 removeWindow核心逻辑
    • 08.Surface与窗口的关系
      • 8.1 Surface的本质
      • 8.2 Surface的创建时机
      • 8.3 SurfaceControl事务
    • 09.SurfaceFlinger的合成原理
      • 9.1 SurfaceFlinger的角色
      • 9.2 BufferQueue三缓冲机制
      • 9.3 VSync与帧调度
    • 10.窗口布局与尺寸计算
      • 10.1 布局计算时机
      • 10.2 WindowSurfacePlacer的工作
      • 10.3 Insets系统
    • 11.输入法窗口的特殊管理
      • 11.1 输入法窗口的特殊性
      • 11.2 输入法窗口的层级管理
      • 11.3 输入法弹出的窗口调整
    • 12.Dialog与PopupWindow的窗口原理
      • 12.1 Dialog的窗口机制
      • 12.2 为什么不能用Application Context显示Dialog
      • 12.3 PopupWindow的窗口机制
    • 13.窗口动画系统
      • 13.1 动画类型
      • 13.2 动画系统架构
      • 13.3 窗口动画的执行
    • 14.多屏幕与折叠屏窗口管理
      • 14.1 多屏幕架构
      • 14.2 分屏模式的实现
      • 14.3 折叠屏的窗口适配
    • 15.WindowContainer层级树详解
      • 15.1 WindowContainer继承体系
      • 15.2 Z-Order的计算方式
      • 15.3 完整的窗口层级树示例
    • 16.WMS的性能优化与调试
      • 16.1 WMS相关的性能问题
      • 16.2 WMS调试命令
      • 16.3 使用Systrace分析WMS
    • 17.总结与技术思考
      • 17.1 核心要点
      • 17.2 WMS与其他服务的关系
      • 17.3 学习建议
    • 18.Window与DecorView创建过程
      • 18.1 Activity组件UI实现
      • 18.2 PhoneWindow创建过程
      • 18.3 DecorView创建过程
      • 18.4 ViewRootImpl创建过程
      • 18.5 Dialog中Window创建
      • 18.6 四者关系总结

    # 01.为什么需要窗口管理系统

    # 1.1 多窗口的核心挑战

    在Android系统中,屏幕上同时可能存在多个窗口:Activity窗口、状态栏窗口、导航栏窗口、输入法窗口、Toast窗口、Dialog窗口等。这些窗口需要按照特定的规则进行排列、显示和交互。

    疑惑:为什么不能让每个应用自己管理自己的窗口?

    答疑:如果每个应用自己管理窗口,会产生以下问题:

    • 层级冲突:两个应用都想显示在最上面,谁说了算?
    • 输入抢占:屏幕上有多个窗口,触摸事件应该发给谁?
    • 资源浪费:每个窗口都直接操作硬件显示,效率极低
    • 安全隐患:恶意应用可以覆盖系统UI,伪造界面

    论证:所以需要一个集中式的窗口管理者——WindowManagerService(WMS),运行在SystemServer进程中,统一管理所有窗口的创建、布局、显示和销毁。

    # 1.2 WMS的核心职责

    WMS的职责:
    ├── 窗口管理:创建、销毁、布局、层级排序
    ├── 窗口动画:转场动画、窗口动画、系统动画
    ├── 输入管理:将输入事件分发到正确的窗口
    ├── Surface管理:管理窗口的绘制表面
    ├── 焦点管理:决定哪个窗口获得输入焦点
    └── 屏幕管理:多屏幕、分屏、画中画
    
    1
    2
    3
    4
    5
    6
    7

    # 1.3 WMS在系统中的位置

    WMS在系统架构中的位置:
    
                         应用层
                ┌──────────────────┐
                │  WindowManager   │  ← 应用通过WindowManager接口添加窗口
                │  (ViewManager)   │
                └────────┬─────────┘
                         │ Binder IPC
                ┌────────▼─────────┐
                │       WMS        │  ← 窗口逻辑管理
                │  (SystemServer)  │
                └────────┬─────────┘
                         │
                ┌────────▼─────────┐
                │  SurfaceFlinger  │  ← 图层合成与显示
                │   (Native进程)    │
                └────────┬─────────┘
                         │
                ┌────────▼─────────┐
                │  Hardware (HWC)  │  ← 硬件合成器
                │   (HAL层)        │
                └──────────────────┘
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22

    # 02.WMS的整体架构设计

    # 2.1 核心类关系

    WMS核心类关系:
    ┌──────────────────────────────────────────────┐
    │            WindowManagerService               │
    │  ┌───────────────────────────────────────┐   │
    │  │      RootWindowContainer              │   │
    │  │  ├── DisplayContent (屏幕)            │   │
    │  │  │   ├── DisplayArea.Root             │   │
    │  │  │   │   ├── TaskDisplayArea          │   │
    │  │  │   │   │   └── Task                 │   │
    │  │  │   │   │       └── ActivityRecord   │   │
    │  │  │   │   │           └── WindowState  │   │
    │  │  │   │   ├── DisplayArea (StatusBar)  │   │
    │  │  │   │   │   └── WindowToken          │   │
    │  │  │   │   │       └── WindowState      │   │
    │  │  │   │   └── DisplayArea (NavBar)     │   │
    │  │  │   │       └── WindowToken          │   │
    │  │  │   └── ImeContainer                 │   │
    │  │  │       └── WindowState (输入法)      │   │
    │  │  └── DisplayContent (第二屏幕)         │   │
    │  └───────────────────────────────────────┘   │
    │  ┌────────────────┐ ┌───────────────────┐    │
    │  │ WindowAnimator  │ │ InputManagerSvc   │    │
    │  │ (动画管理)       │ │ (输入事件分发)     │    │
    │  └────────────────┘ └───────────────────┘    │
    │  ┌────────────────┐ ┌───────────────────┐    │
    │  │ WindowSurfacePl │ │ AccessibilityCtrl │    │
    │  │ acer (布局计算)  │ │ (无障碍)          │    │
    │  └────────────────┘ └───────────────────┘    │
    └──────────────────────────────────────────────┘
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29

    # 2.2 WMS的线程模型

    WMS的操作必须在特定线程中执行,这是保证线程安全的关键:

    // WindowManagerService.java
    class WindowManagerService extends IWindowManager.Stub {
        // WMS使用AMS的Handler线程(android.display线程)
        final Handler mH;
        
        // 全局锁,WMS所有操作都需要持有此锁
        final WindowManagerGlobalLock mGlobalLock;
        
        // 动画线程
        final WindowAnimator mAnimator;
        
        // SurfaceFlinger事务处理线程
        final SurfaceAnimationRunner mSurfaceAnimationRunner;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    WMS使用单一锁mGlobalLock(在Android 10+中与ATMS共享同一个锁)来保护所有窗口状态的修改。这意味着所有窗口操作都是串行的,避免了复杂的并发问题。

    # 2.3 Session机制

    每个应用进程与WMS之间通过Session通信:

    // Session.java - 应用进程与WMS的会话
    class Session extends IWindowSession.Stub {
        final WindowManagerService mService;
        final int mUid;               // 应用UID
        final int mPid;               // 应用PID
        SurfaceSession mSurfaceSession; // 与SurfaceFlinger的连接
        
        // 添加窗口
        @Override
        public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
                int viewVisibility, int displayId, int userId,
                InsetsVisibilities requestedVisibilities, InputChannel outInputChannel,
                InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
            return mService.addWindow(this, window, attrs, viewVisibility, displayId,
                    userId, requestedVisibilities, outInputChannel, outInsetsState,
                    outActiveControls);
        }
        
        // 移除窗口
        @Override
        public void remove(IWindow window) {
            mService.removeWindow(this, window);
        }
        
        // 更新窗口布局
        @Override
        public int relayout(IWindow window, WindowManager.LayoutParams attrs,
                int requestedWidth, int requestedHeight, int viewVisibility, ...) {
            return mService.relayoutWindow(this, window, attrs, 
                    requestedWidth, requestedHeight, ...);
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32

    # 03.WMS的启动与初始化

    # 3.1 WMS的创建时机

    WMS在SystemServer的startOtherServices阶段创建:

    // SystemServer.java
    private void startOtherServices() {
        // WMS在AMS之后、其他服务之前创建
        WindowManagerService wm = WindowManagerService.main(context, 
                inputManager, !mFirstBoot, mOnlyCore,
                new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
        
        // 注册到ServiceManager
        ServiceManager.addService(Context.WINDOW_SERVICE, wm, false,
                DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
        
        // 与InputManager关联
        inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());
        
        // 初始化显示
        wm.displayReady();
        
        // 系统启动完成后
        wm.systemReady();
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

    # 3.2 WMS构造过程

    // WindowManagerService.java
    private WindowManagerService(Context context, InputManagerService inputManager,
            boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy,
            ActivityTaskManagerService atm) {
        
        // 1.初始化核心组件
        mContext = context;
        mInputManager = inputManager;
        mPolicy = policy;  // PhoneWindowManager
        mAtmService = atm;
        
        // 2.初始化全局锁(与ATMS共享)
        mGlobalLock = atm.getGlobalLock();
        
        // 3.创建动画器
        mAnimator = new WindowAnimator(this);
        mSurfaceAnimationRunner = new SurfaceAnimationRunner(
                mTransactionFactory, mPowerManagerInternal);
        
        // 4.创建窗口容器根节点
        mRoot = new RootWindowContainer(this);
        
        // 5.初始化窗口布局器
        mWindowPlacerLocked = new WindowSurfacePlacer(this);
        
        // 6.创建DisplayManager连接
        mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
        mDisplayManager.registerDisplayListener(this, null);
        
        // 7.初始化策略
        mPolicy.init(context, this, mActivityManager);
        
        // 8.初始化DragAndDrop控制器
        mDragDropController = new DragDropController(this, mH.getLooper());
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35

    # 3.3 Display初始化

    // WindowManagerService.java
    public void displayReady() {
        // 获取所有显示设备
        final int[] displayIds = mDisplayManager.getDisplayIds();
        
        for (int displayId : displayIds) {
            // 为每个显示设备创建DisplayContent
            final Display display = mDisplayManager.getDisplay(displayId);
            createDisplayContentLocked(display);
        }
        
        synchronized (mGlobalLock) {
            // 配置默认显示设备
            final DisplayContent displayContent = getDefaultDisplayContentLocked();
            
            // 读取显示设置
            readForcedDisplayPropertiesLocked(displayContent);
            
            // 初始化系统UI区域
            mPolicy.setInitialDisplaySize(
                    displayContent.getDisplay(),
                    displayContent.mBaseDisplayWidth,
                    displayContent.mBaseDisplayHeight,
                    displayContent.mBaseDisplayDensity);
        }
        
        // 标记显示已就绪
        mDisplayReady = true;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29

    # 04.Window的类型与层级体系

    # 4.1 窗口类型分类

    Android将窗口分为三大类,每类有不同的层级范围:

    // WindowManager.LayoutParams 中的窗口类型定义
    public static class LayoutParams {
        // === 应用窗口 (1~99) ===
        public static final int FIRST_APPLICATION_WINDOW = 1;
        public static final int TYPE_BASE_APPLICATION   = 1;    // 应用基础窗口
        public static final int TYPE_APPLICATION        = 2;    // 普通Activity窗口
        public static final int TYPE_APPLICATION_STARTING = 3;  // 启动窗口(Splash)
        public static final int TYPE_DRAWN_APPLICATION  = 4;    // 绘制完成的应用窗口
        public static final int LAST_APPLICATION_WINDOW  = 99;
        
        // === 子窗口 (1000~1999) ===
        public static final int FIRST_SUB_WINDOW        = 1000;
        public static final int TYPE_APPLICATION_PANEL   = 1000; // 面板窗口
        public static final int TYPE_APPLICATION_MEDIA   = 1001; // 媒体窗口
        public static final int TYPE_APPLICATION_SUB_PANEL = 1002;
        public static final int TYPE_APPLICATION_ATTACHED_DIALOG = 1003; // 附属Dialog
        public static final int TYPE_APPLICATION_MEDIA_OVERLAY = 1004;
        public static final int LAST_SUB_WINDOW         = 1999;
        
        // === 系统窗口 (2000~2999) ===
        public static final int FIRST_SYSTEM_WINDOW     = 2000;
        public static final int TYPE_STATUS_BAR         = 2000;  // 状态栏
        public static final int TYPE_SEARCH_BAR         = 2001;  // 搜索栏
        public static final int TYPE_PHONE              = 2002;  // 来电窗口
        public static final int TYPE_SYSTEM_ALERT       = 2003;  // 系统弹窗
        public static final int TYPE_TOAST              = 2005;  // Toast
        public static final int TYPE_SYSTEM_OVERLAY     = 2006;  // 系统覆盖层
        public static final int TYPE_INPUT_METHOD       = 2011;  // 输入法
        public static final int TYPE_WALLPAPER          = 2013;  // 壁纸
        public static final int TYPE_STATUS_BAR_PANEL   = 2014;  // 状态栏面板
        public static final int TYPE_APPLICATION_OVERLAY = 2038; // 应用悬浮窗(Android 8.0+)
        public static final int TYPE_NAVIGATION_BAR     = 2019;  // 导航栏
        public static final int LAST_SYSTEM_WINDOW      = 2999;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34

    # 4.2 窗口层级排序

    WMS使用Z-Order来确定窗口的显示顺序。层级越高的窗口显示在越上面:

    屏幕上窗口的Z-Order层级(从高到低):
    
    Layer 36: TYPE_POINTER (鼠标指针)
    Layer 35: TYPE_NAVIGATION_BAR_PANEL
    Layer 34: TYPE_SECURE_SYSTEM_OVERLAY  
    Layer 33: TYPE_BOOT_PROGRESS (开机动画)
    Layer 32: TYPE_DISPLAY_OVERLAY
    ...
    Layer 25: TYPE_STATUS_BAR (状态栏)
    Layer 24: TYPE_STATUS_BAR_PANEL
    ...
    Layer 20: TYPE_NAVIGATION_BAR (导航栏)
    ...
    Layer 17: TYPE_VOLUME_OVERLAY (音量条)
    Layer 16: TYPE_SYSTEM_OVERLAY
    Layer 15: TYPE_APPLICATION_OVERLAY (悬浮窗)
    Layer 14: TYPE_TOAST
    ...
    Layer 12: TYPE_INPUT_METHOD (输入法)
    Layer 11: TYPE_INPUT_METHOD_DIALOG
    ...
    Layer  5: TYPE_SYSTEM_ALERT
    Layer  4: TYPE_PHONE
    ...
    Layer  2: TYPE_APPLICATION (Activity窗口)
    Layer  1: TYPE_BASE_APPLICATION
    Layer  0: TYPE_WALLPAPER (壁纸)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27

    WMS中层级排序的实现:

    // WindowManagerPolicy.java (PhoneWindowManager)
    // 将窗口类型映射到层级值
    default int getWindowLayerFromTypeLw(int type) {
        if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
            return APPLICATION_LAYER;  // 2
        }
        switch (type) {
            case TYPE_WALLPAPER:
                return 1;
            case TYPE_PHONE:
                return 3;
            case TYPE_SEARCH_BAR:
            case TYPE_VOICE_INTERACTION_STARTING:
                return 4;
            case TYPE_SYSTEM_ALERT:
                return 5;
            case TYPE_TOAST:
                return 8;
            case TYPE_INPUT_METHOD:
                return 12;
            case TYPE_APPLICATION_OVERLAY:
                return 15;
            case TYPE_STATUS_BAR:
                return 25;
            case TYPE_NAVIGATION_BAR:
                return 20;
            // ...
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29

    # 4.3 子窗口的层级计算

    子窗口的层级基于父窗口计算:

    子窗口层级规则:
    父窗口层级 = Application Layer (2)
    
    子窗口层级 = 父窗口层级的相对偏移
      TYPE_APPLICATION_PANEL (-2)     → 在父窗口下方
      TYPE_APPLICATION_MEDIA (-1)     → 在父窗口下方
      TYPE_APPLICATION_SUB_PANEL (1)  → 在父窗口上方
      TYPE_APPLICATION_ATTACHED_DIALOG (1) → 在父窗口上方
    
    示例:SurfaceView的窗口
      父窗口(Activity): Z = 某个值
      SurfaceView窗口: Z = 父窗口Z - 1 (TYPE_APPLICATION_MEDIA)
      这就是为什么SurfaceView显示在Activity内容之下,通过"挖洞"方式显示
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    # 05.WindowState的核心数据结构

    # 5.1 WindowState详解

    WindowState是WMS中描述每个窗口的核心数据结构,类似于AMS中的ActivityRecord:

    class WindowState extends WindowContainer<WindowState> {
        // === 身份标识 ===
        final IWindow mClient;           // 应用端的IWindow.Stub
        final WindowToken mToken;        // 所属的WindowToken
        final Session mSession;          // 所属的会话
        final WindowManager.LayoutParams mAttrs; // 窗口属性
        final int mBaseLayer;            // 基础层级
        final int mSubLayer;             // 子层级
        
        // === 窗口尺寸 ===
        final Rect mRequestedWidth;      // 请求的宽度
        final Rect mRequestedHeight;     // 请求的高度
        final Rect mFrame;               // 最终的窗口位置和大小
        final Rect mDisplayFrame;        // 显示区域
        final Rect mContentFrame;        // 内容区域
        final Rect mVisibleFrame;        // 可见区域
        
        // === 窗口状态 ===
        boolean mHasSurface;             // 是否有Surface
        boolean mRemoved;                // 是否已移除
        boolean mViewVisibility;         // 视图可见性
        boolean mPolicyVisibility;       // 策略可见性
        int mAnimatingExit;              // 是否正在退出动画
        
        // === Surface相关 ===
        WindowSurfaceController mWinAnimator; // 动画控制器
        SurfaceControl mSurfaceControl;       // Surface控制句柄
        
        // === 输入相关 ===
        final InputWindowHandle mInputWindowHandle; // 输入窗口句柄
        boolean mRemoveOnExit;           // 退出时是否移除
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32

    # 5.2 WindowToken的作用

    WindowToken是窗口的分组令牌,同一个WindowToken下的窗口属于同一个逻辑组:

    // WindowToken.java
    class WindowToken extends WindowContainer<WindowState> {
        final IBinder token;          // 令牌标识
        final int windowType;         // 窗口类型
        
        // Activity的窗口对应的是ActivityRecord(ActivityRecord继承自WindowToken)
        // 系统窗口对应的是WindowToken本身
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    WindowToken的组织关系:
    
    Activity窗口:
      ActivityRecord(extends WindowToken)
        ├── WindowState (主窗口)
        ├── WindowState (子窗口:Dialog)
        └── WindowState (子窗口:PopupWindow)
    
    输入法窗口:
      WindowToken(TYPE_INPUT_METHOD)
        └── WindowState (输入法窗口)
    
    Toast窗口:
      WindowToken(TYPE_TOAST)
        └── WindowState (Toast窗口)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    # 5.3 DisplayContent的结构

    每个物理屏幕对应一个DisplayContent,它是窗口层级树的根:

    class DisplayContent extends RootDisplayArea {
        final int mDisplayId;             // 显示设备ID
        private final Display mDisplay;    // Display对象
        
        // 显示尺寸信息
        int mBaseDisplayWidth;
        int mBaseDisplayHeight;
        int mBaseDisplayDensity;
        
        // 旋转状态
        int mRotation;
        int mLastOrientation;
        
        // 焦点窗口
        WindowState mCurrentFocus;
        WindowState mLastFocus;
        
        // 输入法目标窗口
        WindowState mInputMethodTarget;
        
        // 壁纸目标窗口
        WindowState mWallpaperTarget;
        
        // 显示区域布局
        DisplayArea.Root mRootDisplayArea;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26

    # 06.addWindow的完整流程

    # 6.1 应用端发起addWindow

    当应用调用WindowManager.addView()时,最终会触发WMS的addWindow:

    addWindow的完整调用链:
    
    应用进程                                    SystemServer进程
    WindowManager.addView(view, params)
      │
      └── WindowManagerImpl.addView()
            └── WindowManagerGlobal.addView()
                  ├── 创建ViewRootImpl
                  └── ViewRootImpl.setView()
                        ├── 1.requestLayout() → 触发首次布局
                        └── 2.mWindowSession.addToDisplay()
                              │
                              └── ──Binder──► Session.addToDisplayAsUser()
                                                │
                                                └── WMS.addWindow()
                                                      ├── 检查权限
                                                      ├── 创建WindowState
                                                      ├── 添加到窗口树
                                                      ├── 创建InputChannel
                                                      └── 创建SurfaceSession
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

    # 6.2 WMS.addWindow核心逻辑

    // WindowManagerService.java
    public int addWindow(Session session, IWindow client, 
            LayoutParams attrs, int viewVisibility, int displayId,
            int requestUserId, InsetsVisibilities requestedVisibilities,
            InputChannel outInputChannel, InsetsState outInsetsState,
            InsetsSourceControl[] outActiveControls) {
        
        // 1.权限检查
        int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay,
                attrs.packageName, appOp);
        if (res != WindowManagerGlobal.ADD_OKAY) {
            return res;  // 无权限,拒绝添加
        }
        
        synchronized (mGlobalLock) {
            // 2.获取DisplayContent
            final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
            if (displayContent == null) {
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }
            
            // 3.检查窗口是否已存在
            if (mWindowMap.containsKey(client.asBinder())) {
                return WindowManagerGlobal.ADD_DUPLICATE_ADD;
            }
            
            // 4.查找WindowToken
            WindowToken token = displayContent.getWindowToken(attrs.token);
            
            // 5.对于应用窗口,token必须已存在(由AMS创建)
            if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
                ActivityRecord activity = token != null ? token.asActivityRecord() : null;
                if (activity == null) {
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            }
            
            // 6.对于系统窗口,可能需要创建token
            if (token == null) {
                if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                token = new WindowToken.Builder(this, client.asBinder(), type)
                        .setDisplayContent(displayContent)
                        .build();
            }
            
            // 7.创建WindowState
            final WindowState win = new WindowState(this, session, client, token,
                    parentWindow, appOp, attrs, viewVisibility, session.mUid,
                    session.mCanAddInternalSystemWindow);
            
            // 8.检查窗口是否可以添加
            res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);
            if (res != ADD_OKAY) {
                return res;
            }
            
            // 9.创建输入通道(用于接收触摸事件)
            final boolean openInputChannels = (outInputChannel != null 
                    && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
            if (openInputChannels) {
                win.openInputChannel(outInputChannel);
            }
            
            // 10.将WindowState添加到窗口树
            win.mToken.addWindow(win);
            
            // 11.注册到全局Map
            mWindowMap.put(client.asBinder(), win);
            
            // 12.设置输入窗口
            win.mInputWindowHandle.setFocusable(focusable);
            mInputManagerService.registerInputChannel(win.mInputChannel);
            
            // 13.如果窗口可见,准备Surface
            if (win.isVisible()) {
                displayContent.assignWindowLayers(false);
            }
            
            // 14.更新焦点窗口
            if (focusChanged) {
                mInputManagerService.setInputWindows(
                        displayContent.getInputWindowHandleList(), displayContent);
            }
        }
        
        return res;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89

    # 6.3 addWindow的安全检查

    WMS在添加窗口时会进行严格的安全检查:

    // PhoneWindowManager.java
    public int checkAddPermission(int type, boolean isRoundedCornerOverlay,
            String packageName, int[] outAppOp) {
        
        // 应用窗口和子窗口不需要特殊权限
        if (type < FIRST_SYSTEM_WINDOW || type > LAST_SYSTEM_WINDOW) {
            return ADD_OKAY;
        }
        
        // 系统窗口需要检查权限
        switch (type) {
            case TYPE_TOAST:
                // Toast不需要权限,但有数量限制
                outAppOp[0] = OP_TOAST_WINDOW;
                return ADD_OKAY;
                
            case TYPE_APPLICATION_OVERLAY:
                // 悬浮窗需要SYSTEM_ALERT_WINDOW权限
                if (!Settings.canDrawOverlays(mContext)) {
                    return ADD_PERMISSION_DENIED;
                }
                outAppOp[0] = OP_SYSTEM_ALERT_WINDOW;
                return ADD_OKAY;
                
            case TYPE_INPUT_METHOD:
            case TYPE_STATUS_BAR:
            case TYPE_NAVIGATION_BAR:
                // 需要INTERNAL_SYSTEM_WINDOW权限(仅系统应用)
                if (mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW)
                        != PERMISSION_GRANTED) {
                    return ADD_PERMISSION_DENIED;
                }
                return ADD_OKAY;
                
            default:
                // 其他系统窗口需要SYSTEM_ALERT_WINDOW权限
                return mContext.checkCallingOrSelfPermission(SYSTEM_ALERT_WINDOW)
                        == PERMISSION_GRANTED ? ADD_OKAY : ADD_PERMISSION_DENIED;
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40

    # 07.removeWindow的流程

    # 7.1 窗口移除的触发方式

    窗口移除可以由多方触发:

    窗口移除的触发场景:
    ├── 应用主动移除
    │   └── WindowManager.removeView() → ViewRootImpl.die() → WMS.removeWindow()
    │
    ├── AMS触发移除(Activity销毁)
    │   └── AMS.destroyActivity() → ActivityRecord.removeWindow()
    │
    ├── WMS主动移除
    │   ├── 进程死亡 → WMS.handleAppDiedLocked()
    │   └── 会话断开 → Session.onTransact(BINDER_DIED)
    │
    └── 系统策略移除
        └── 屏幕关闭、用户切换等
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    # 7.2 removeWindow核心逻辑

    // WindowManagerService.java
    void removeWindow(Session session, IWindow client) {
        synchronized (mGlobalLock) {
            WindowState win = windowForClientLocked(session, client, false);
            if (win != null) {
                win.removeIfPossible();
            }
        }
    }
    
    // WindowState.java
    void removeIfPossible() {
        // 检查是否可以立即移除
        if (!removeIfPossible(false)) {
            // 如果窗口还在做动画,延迟移除
            return;
        }
    }
    
    boolean removeIfPossible(boolean force) {
        // 1.检查是否还有退出动画
        if (mAnimatingExit && !force) {
            mRemoveOnExit = true;  // 动画结束后移除
            return false;
        }
        
        // 2.检查是否正在进行Surface操作
        if (isAnimating(TRANSITION | PARENTS)) {
            if (!force) {
                mRemoveOnExit = true;
                return false;
            }
        }
        
        // 3.执行实际移除
        removeImmediately();
        return true;
    }
    
    void removeImmediately() {
        // 1.标记已移除
        mRemoved = true;
        
        // 2.销毁Surface
        if (mHasSurface) {
            destroySurfaceLocked();
        }
        
        // 3.关闭输入通道
        if (mInputChannel != null) {
            mInputChannel.dispose();
            mInputChannel = null;
        }
        
        // 4.从WindowToken中移除
        mToken.removeChild(this);
        
        // 5.从全局Map中移除
        mService.mWindowMap.remove(mClient.asBinder());
        
        // 6.更新焦点
        if (mService.mCurrentFocus == this) {
            mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true);
        }
        
        // 7.通知InputManager窗口已移除
        mService.mInputManagerService.removeInputChannel(mInputWindowHandle.token);
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68

    # 08.Surface与窗口的关系

    # 8.1 Surface的本质

    Surface是窗口绘制内容的载体,本质上是一块共享内存区域(GraphicBuffer):

    Surface在系统中的位置:
    
    应用进程                  SystemServer             SurfaceFlinger
    ┌──────────┐           ┌────────────┐           ┌──────────────┐
    │ Canvas    │           │ SurfaceCtrl│           │ Layer         │
    │  │        │           │   │        │           │  │            │
    │  ▼        │           │   ▼        │           │  ▼            │
    │ Surface ──┼──共享内存──┼──Surface───┼──Binder──►│ BufferQueue   │
    │           │           │            │           │  │            │
    │ 绘制内容  │           │ 管理属性    │           │  ▼            │
    │ (draw)    │           │ (位置/大小) │           │ GraphicBuffer │
    │           │           │            │           │ (实际显存)     │
    └──────────┘           └────────────┘           └──────────────┘
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    # 8.2 Surface的创建时机

    Surface在窗口首次需要显示时创建:

    // WindowStateAnimator.java
    WindowSurfaceController createSurfaceLocked() {
        if (mSurfaceController != null) {
            return mSurfaceController;
        }
        
        // 1.确定Surface的尺寸
        int w = mWin.mCompatFrame.width();
        int h = mWin.mCompatFrame.height();
        
        // 2.创建SurfaceControl
        // SurfaceControl是应用进程控制Surface属性(位置、大小、层级)的句柄
        mSurfaceController = new WindowSurfaceController(mWin.mSession.mSurfaceSession,
                mWin.mAttrs.getTitle().toString(), w, h, format, flags, this,
                windowType, ownerUid);
        
        // 3.设置Surface的初始属性
        mSurfaceController.setPositionInTransaction(mWin.mFrame.left, mWin.mFrame.top);
        mSurfaceController.setLayer(mWin.mLayer);
        
        mWin.mHasSurface = true;
        
        return mSurfaceController;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24

    # 8.3 SurfaceControl事务

    WMS通过SurfaceControl.Transaction来批量修改Surface属性:

    // WMS中使用Transaction修改窗口属性
    void applySurfaceChangesTransaction() {
        SurfaceControl.Transaction t = mTransaction;
        
        // 批量设置多个窗口的属性
        for (WindowState win : mVisibleWindows) {
            // 设置位置
            t.setPosition(win.mSurfaceControl, win.mFrame.left, win.mFrame.top);
            
            // 设置大小
            t.setBufferSize(win.mSurfaceControl, win.mFrame.width(), win.mFrame.height());
            
            // 设置层级
            t.setLayer(win.mSurfaceControl, win.mLayer);
            
            // 设置透明度
            t.setAlpha(win.mSurfaceControl, win.mAlpha);
            
            // 设置变换矩阵(旋转、缩放)
            t.setMatrix(win.mSurfaceControl, 
                    win.mDtDx, win.mDtDy, win.mDsDx, win.mDsDy);
        }
        
        // 一次性提交所有修改(原子操作)
        t.apply();
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26

    关键点:Transaction是原子操作,所有窗口属性的修改在同一帧生效,避免了画面撕裂。

    # 09.SurfaceFlinger的合成原理

    # 9.1 SurfaceFlinger的角色

    SurfaceFlinger是Android显示系统的核心守护进程,负责将所有窗口的Surface合成为最终的屏幕画面:

    SurfaceFlinger的工作流程:
    
    每一帧的处理过程:
    1. 收集所有Layer的Buffer
       ├── 应用窗口Layer
       ├── 状态栏Layer
       ├── 导航栏Layer
       └── 输入法Layer
    
    2. 计算可见区域
       ├── 确定每个Layer的可见部分
       ├── 被遮挡的部分不需要合成
       └── 透明区域需要Alpha混合
    
    3. 选择合成方式
       ├── GPU合成(OpenGL ES)
       │   └── 适用于复杂动画、Alpha混合
       └── HWC合成(Hardware Composer)
           └── 适用于简单叠加,效率更高
    
    4. 输出到Display
       └── 通过HWC HAL将合成结果送到屏幕
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22

    # 9.2 BufferQueue三缓冲机制

    应用绘制的内容通过BufferQueue传递给SurfaceFlinger:

    BufferQueue三缓冲机制:
    
             应用进程                    SurfaceFlinger
        (Buffer生产者)                  (Buffer消费者)
             │                              │
             ├── dequeueBuffer() ──►┐      │
             │   获取空闲Buffer      │      │
             │                      │      │
             ├── 绘制内容到Buffer    │ BufferQueue │
             │   (Canvas/OpenGL)    │ (3个Buffer) │
             │                      │      │
             ├── queueBuffer() ────►┤      │
             │   提交已绘制Buffer    │      │
             │                      │      │
             │                      ├──► acquireBuffer()
             │                      │    获取已绘制Buffer
             │                      │
             │                      ├──► 合成到屏幕
             │                      │
             │                      ├──► releaseBuffer()
             │                      │    释放已合成Buffer
             │                      │
    三缓冲:Buffer A (正在显示), Buffer B (已绘制等待合成), Buffer C (正在绘制)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23

    # 9.3 VSync与帧调度

    SurfaceFlinger通过VSync信号来同步显示流水线:

    VSync信号的两阶段偏移:
    
    VSync           VSync           VSync
      │               │               │
      ├──VSYNC-app    ├──VSYNC-app    ├──
      │  │            │  │            │
      │  └──应用绘制   │  └──应用绘制   │
      │    Frame N+1  │    Frame N+2  │
      │               │               │
      ├──VSYNC-sf     ├──VSYNC-sf     ├──
      │  │            │  │            │
      │  └──SF合成     │  └──SF合成     │
      │    Frame N    │    Frame N+1  │
      │               │               │
      └──Display      └──Display      └──
         Frame N-1       Frame N         Frame N+1
    
    VSYNC-app: 触发应用开始绘制下一帧
    VSYNC-sf:  触发SurfaceFlinger合成已完成的帧
    偏移设计:  避免应用绘制和SF合成竞争同一个Buffer
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

    # 10.窗口布局与尺寸计算

    # 10.1 布局计算时机

    WMS在以下情况会触发窗口布局计算:

    触发布局计算的事件:
    ├── 窗口添加/移除
    ├── 窗口尺寸变化(relayout)
    ├── 屏幕旋转
    ├── 系统UI变化(状态栏显示/隐藏)
    ├── 输入法显示/隐藏
    ├── 分屏模式切换
    └── 配置变化
    
    1
    2
    3
    4
    5
    6
    7
    8

    # 10.2 WindowSurfacePlacer的工作

    // WindowSurfacePlacer.java - 窗口布局的核心类
    class WindowSurfacePlacer {
        // 执行布局计算
        void performSurfacePlacement() {
            if (mInLayout) return;  // 防止重入
            mInLayout = true;
            
            try {
                performSurfacePlacementLoop();
            } finally {
                mInLayout = false;
            }
        }
        
        void performSurfacePlacementLoop() {
            int loopCount = 6;  // 最多循环6次,防止死循环
            
            do {
                mTraversalScheduled = false;
                
                // 核心布局过程
                performSurfacePlacementNoTrace();
                
            } while (mTraversalScheduled && loopCount > 0);
        }
        
        void performSurfacePlacementNoTrace() {
            // 1.准备阶段:收集所有需要布局的窗口
            mService.mRoot.prepareSurfaces();
            
            // 2.布局阶段:计算每个窗口的位置和大小
            mService.mRoot.performLayout(false, false);
            
            // 3.应用阶段:将计算结果应用到Surface
            mService.mRoot.applySurfaceChangesTransaction();
            
            // 4.通知阶段:通知应用窗口尺寸变化
            mService.mRoot.handleNotObscuredLocked(
                    defaultDisplay.mWinSizeChanged,
                    defaultDisplay.mTmpApplySurfaceChangesTransactionState);
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42

    # 10.3 Insets系统

    Android 11+引入了全新的Insets系统替代原来的fitSystemWindows机制:

    // InsetsState.java - 记录所有系统UI区域
    class InsetsState {
        // 不同类型的Insets源
        private final SparseArray<InsetsSource> mSources = new SparseArray<>();
        
        // Insets类型
        static final int ITYPE_STATUS_BAR = 0;      // 状态栏
        static final int ITYPE_NAVIGATION_BAR = 1;   // 导航栏
        static final int ITYPE_IME = 2;              // 输入法
        static final int ITYPE_CAPTION_BAR = 3;      // 标题栏
        
        // 计算窗口可用区域
        Insets calculateInsets(Rect frame, boolean ignoreVisibility) {
            Insets result = Insets.NONE;
            for (int i = 0; i < mSources.size(); i++) {
                InsetsSource source = mSources.valueAt(i);
                if (source.isVisible() || ignoreVisibility) {
                    result = Insets.max(result, source.calculateInsets(frame, false));
                }
            }
            return result;
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    Insets如何影响窗口布局:
    
    ┌───────────────────────────────┐  ← 屏幕顶部
    │        状态栏 Inset            │  (ITYPE_STATUS_BAR)
    ├───────────────────────────────┤
    │                               │
    │                               │
    │       应用可用区域              │  ← 扣除Insets后的区域
    │       (Content Area)          │
    │                               │
    │                               │
    ├───────────────────────────────┤
    │        导航栏 Inset            │  (ITYPE_NAVIGATION_BAR)
    └───────────────────────────────┘  ← 屏幕底部
    
    当输入法弹出时:
    ├───────────────────────────────┤
    │       应用可用区域(缩小)      │
    ├───────────────────────────────┤
    │                               │
    │        输入法 Inset            │  (ITYPE_IME)
    │                               │
    ├───────────────────────────────┤
    │        导航栏 Inset            │
    └───────────────────────────────┘
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25

    # 11.输入法窗口的特殊管理

    # 11.1 输入法窗口的特殊性

    输入法窗口在WMS中有特殊的管理逻辑:

    输入法窗口的特殊性:
    1. 不属于任何应用,但显示在应用上方
    2. 需要跟随目标窗口调整位置
    3. 需要特殊的动画效果
    4. 层级介于应用窗口和系统窗口之间
    
    1
    2
    3
    4
    5

    # 11.2 输入法窗口的层级管理

    // DisplayContent.java
    // 输入法窗口有专门的容器
    class DisplayContent extends RootDisplayArea {
        // 输入法容器
        final ImeContainer mImeWindowsContainer;
        
        // 输入法目标窗口
        WindowState mInputMethodTarget;
        
        // 重新定位输入法窗口
        void computeImeTarget(boolean updateImeTarget) {
            // 查找需要输入法的窗口(获得焦点且接受输入的窗口)
            WindowState target = null;
            
            for (WindowState ws : mWindows) {
                if (ws.canBeImeTarget()) {
                    target = ws;
                    break;
                }
            }
            
            if (target != null && target != mInputMethodTarget) {
                mInputMethodTarget = target;
                // 将输入法窗口重新定位到目标窗口上方
                assignImeWindowLayer();
            }
        }
        
        // 调整输入法层级,确保显示在目标窗口上方
        void assignImeWindowLayer() {
            // 输入法层级 = 目标窗口层级 + 1
            // 但始终在系统窗口(状态栏、导航栏)下方
            mImeWindowsContainer.assignLayer(mTransaction, 
                    mInputMethodTarget.mSubLayer + 1);
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36

    # 11.3 输入法弹出的窗口调整

    当输入法弹出时,WMS需要调整其他窗口的布局:

    // 输入法弹出时的窗口调整流程
    void onImeShown(WindowState imeWindow) {
        // 1.计算输入法占用的区域
        Rect imeFrame = imeWindow.getFrame();
        
        // 2.更新InsetsState
        InsetsSource imeSource = new InsetsSource(ITYPE_IME);
        imeSource.setFrame(imeFrame);
        imeSource.setVisible(true);
        
        // 3.通知所有受影响的窗口
        for (WindowState ws : mVisibleWindows) {
            if (ws.isAffectedByIme()) {
                // 发送InsetsState变化通知
                ws.notifyInsetsChanged();
                // 应用端会根据新的InsetsState重新布局
            }
        }
        
        // 4.如果设置了adjustResize
        // 应用窗口会缩小,内容区域不与输入法重叠
        
        // 5.如果设置了adjustPan
        // 应用窗口不缩小,但整体向上平移
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25

    # 12.Dialog与PopupWindow的窗口原理

    # 12.1 Dialog的窗口机制

    Dialog虽然看起来像一个独立界面,但它本质上是一个依附于Activity的窗口:

    // Dialog.java
    class Dialog {
        private final WindowManager mWindowManager;
        
        Dialog(Context context) {
            // Dialog使用Activity的WindowManager
            mWindowManager = (WindowManager) context.getSystemService(
                    Context.WINDOW_SERVICE);
            
            // 创建PhoneWindow
            mWindow = new PhoneWindow(mContext);
            mWindow.setWindowManager(mWindowManager, null, null);
            
            // 设置窗口属性
            mWindow.setGravity(Gravity.CENTER);
        }
        
        public void show() {
            // 1.执行onCreate
            if (!mCreated) {
                dispatchOnCreate(null);
            }
            
            // 2.执行onStart
            onStart();
            
            // 3.获取DecorView
            mDecor = mWindow.getDecorView();
            
            // 4.设置窗口属性
            WindowManager.LayoutParams l = mWindow.getAttributes();
            l.type = WindowManager.LayoutParams.TYPE_APPLICATION; // 应用窗口类型
            l.token = mContext.getWindow().getAttributes().token; // 使用Activity的token
            
            // 5.通过WindowManager添加窗口
            mWindowManager.addView(mDecor, l);
            
            mShowing = true;
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40

    关键点:Dialog使用Activity的token作为窗口令牌,所以它在WMS中属于Activity的WindowToken分组。

    # 12.2 为什么不能用Application Context显示Dialog

    使用Application Context显示Dialog的问题:
    
    Application Context
      └── 没有Activity的token
          └── WindowManager.addView()
                └── WMS.addWindow()
                      └── 查找WindowToken
                            └── 找不到对应的ActivityRecord
                                  └── 返回BAD_APP_TOKEN错误
                                        └── 抛出BadTokenException
    
    解决方案:
    1. 使用Activity Context
    2. 使用TYPE_APPLICATION_OVERLAY类型(需要悬浮窗权限)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    # 12.3 PopupWindow的窗口机制

    PopupWindow使用子窗口类型:

    // PopupWindow.java
    class PopupWindow {
        void invokePopup(WindowManager.LayoutParams p) {
            // PopupWindow的窗口类型
            p.type = WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL; // 子窗口
            
            // 设置父窗口的token
            p.token = mAnchor.get().getWindowToken();
            
            // 添加窗口
            mWindowManager.addView(mDecorView, p);
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    Dialog vs PopupWindow vs Toast窗口对比:
    
    Dialog:
      类型:TYPE_APPLICATION(应用窗口)
      Token:Activity的token
      特点:独立的PhoneWindow,有完整的生命周期
    
    PopupWindow:
      类型:TYPE_APPLICATION_SUB_PANEL(子窗口)
      Token:锚点View的window token
      特点:依附于父窗口,跟随父窗口移动/销毁
    
    Toast:
      类型:TYPE_TOAST(系统窗口)
      Token:null(由WMS自动创建token)
      特点:不需要Activity,但有显示时长限制
    
    悬浮窗:
      类型:TYPE_APPLICATION_OVERLAY(系统窗口)
      Token:null
      特点:需要SYSTEM_ALERT_WINDOW权限,可以显示在所有应用之上
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21

    # 13.窗口动画系统

    # 13.1 动画类型

    WMS管理的窗口动画主要有以下几类:

    WMS窗口动画类型:
    ├── 窗口进入/退出动画
    │   └── 窗口添加和移除时的动画
    ├── Activity转场动画
    │   └── Activity启动和结束时的窗口转换动画
    ├── 壁纸过渡动画
    │   └── 壁纸切换时的过渡效果
    ├── 旋转动画
    │   └── 屏幕方向改变时的旋转过渡
    ├── 窗口缩放动画
    │   └── 多窗口模式下窗口大小变化
    └── 圆角裁剪动画
        └── 圆角屏幕的边缘裁剪动画
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    # 13.2 动画系统架构

    Android 12+使用Shell Transitions来管理窗口动画:

    // Transition.java (Shell Transitions)
    class Transition {
        // 参与转场的窗口
        final ArraySet<WindowContainer> mParticipants = new ArraySet<>();
        
        // 转场类型
        final int mType;
        
        // 动画开始
        void start() {
            // 1.收集所有参与者的起始状态
            for (WindowContainer wc : mParticipants) {
                mStartStates.put(wc, captureState(wc));
            }
            
            // 2.执行窗口变化(显示/隐藏/移动等)
            applyChanges();
            
            // 3.收集结束状态
            for (WindowContainer wc : mParticipants) {
                mEndStates.put(wc, captureState(wc));
            }
            
            // 4.创建动画
            // 使用起始状态和结束状态之间的差异来计算动画
            TransitionInfo info = calculateTransitionInfo();
            
            // 5.将动画交给Shell执行
            mShellTransitions.startAnimation(info, this::onFinished);
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31

    # 13.3 窗口动画的执行

    // WindowAnimator.java
    class WindowAnimator {
        // 每帧调用一次
        void animate(long frameTimeNs) {
            synchronized (mService.mGlobalLock) {
                // 遍历所有显示设备
                for (int i = mDisplayContentsAnimators.size() - 1; i >= 0; i--) {
                    DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i);
                    
                    // 遍历所有窗口动画
                    displayAnimator.mScreenRotationAnimation.stepAnimationLocked(currentTime);
                    
                    // 更新所有正在做动画的窗口
                    dc.forAllWindows(w -> {
                        WindowStateAnimator winAnimator = w.mWinAnimator;
                        if (winAnimator.isAnimationSet()) {
                            // 计算动画当前帧的变换
                            winAnimator.stepAnimationLocked(currentTime);
                            
                            // 应用变换到Surface
                            SurfaceControl.Transaction t = winAnimator.mTmpTransaction;
                            t.setAlpha(w.mSurfaceControl, winAnimator.mAlpha);
                            t.setMatrix(w.mSurfaceControl, 
                                    winAnimator.mDtDx, winAnimator.mDtDy,
                                    winAnimator.mDsDx, winAnimator.mDsDy);
                            t.setPosition(w.mSurfaceControl,
                                    winAnimator.mPositionX, winAnimator.mPositionY);
                        }
                    }, true);
                }
                
                // 提交所有Surface变更
                mTransaction.apply();
                
                // 如果还有未完成的动画,请求下一帧
                if (hasPendingAnimations()) {
                    scheduleAnimation();
                }
            }
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41

    # 14.多屏幕与折叠屏窗口管理

    # 14.1 多屏幕架构

    WMS天然支持多屏幕,每个屏幕对应一个DisplayContent:

    多屏幕的窗口管理结构:
    
    RootWindowContainer
    ├── DisplayContent (displayId=0, 主屏幕)
    │   ├── TaskDisplayArea
    │   │   ├── Task (全屏App)
    │   │   └── Task (分屏左侧)
    │   ├── DisplayArea (StatusBar)
    │   └── DisplayArea (NavBar)
    │
    ├── DisplayContent (displayId=1, 外接屏幕)
    │   ├── TaskDisplayArea
    │   │   └── Task (投屏App)
    │   └── DisplayArea (StatusBar)
    │
    └── DisplayContent (displayId=2, 虚拟屏幕)
        └── TaskDisplayArea
            └── Task (VirtualDisplay App)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    # 14.2 分屏模式的实现

    分屏模式在WMS中通过多个Root Task实现:

    // 分屏模式的Task结构
    // TaskDisplayArea中同时存在多个可见的Root Task
    class TaskDisplayArea {
        void setLaunchAdjacentFlagRootTask(Task adjacentFlagRootTask) {
            // 设置相邻的分屏Task
            mLaunchAdjacentFlagRootTask = adjacentFlagRootTask;
        }
    }
    
    // 分屏时的布局计算
    void onSplitScreenModeChanged() {
        // 将屏幕区域分为两半
        Rect primaryBounds = new Rect(0, 0, screenWidth / 2, screenHeight);
        Rect secondaryBounds = new Rect(screenWidth / 2, 0, screenWidth, screenHeight);
        
        // 设置两个Root Task的边界
        primaryTask.setBounds(primaryBounds);
        secondaryTask.setBounds(secondaryBounds);
        
        // 创建分割线(Divider)
        mDividerWindow.setPosition(screenWidth / 2 - dividerWidth / 2, 0);
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22

    # 14.3 折叠屏的窗口适配

    折叠屏设备在WMS中有特殊的处理逻辑:

    // 折叠屏展开/折叠的处理
    void onFoldStateChanged(boolean isFolded) {
        if (isFolded) {
            // 折叠:屏幕变小
            // 1.更新DisplayContent的尺寸
            updateDisplaySize(foldedWidth, foldedHeight);
            
            // 2.通知所有窗口配置变化
            mRoot.forAllActivities(activity -> {
                activity.onConfigurationChanged(newConfig);
            });
            
            // 3.可能需要重新布局
            mWindowPlacerLocked.performSurfacePlacement();
        } else {
            // 展开:屏幕变大
            updateDisplaySize(unfoldedWidth, unfoldedHeight);
            
            // Activity可能需要重新创建(如果不支持configChanges)
            // 或者只是调整布局
        }
    }
    
    // 铰链区域的特殊处理
    void computeHingeRegion(DisplayContent dc) {
        // 折叠屏铰链区域可能有遮挡
        Rect hingeRegion = dc.getDisplayInfo().getHingeBounds();
        
        // 在布局计算时避开铰链区域
        for (WindowState ws : dc.mWindows) {
            if (ws.getBounds().intersect(hingeRegion)) {
                // 调整窗口避开铰链区域
                ws.adjustBoundsForHinge(hingeRegion);
            }
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36

    # 15.WindowContainer层级树详解

    # 15.1 WindowContainer继承体系

    WindowContainer是WMS中窗口层级树的基类,理解它的继承关系是理解整个WMS的关键:

    WindowContainer继承体系:
    
    WindowContainer<E extends WindowContainer>
    ├── RootWindowContainer (根节点,整个系统只有一个)
    │
    ├── DisplayContent (代表一个屏幕)
    │   └── extends RootDisplayArea
    │
    ├── DisplayArea (显示区域)
    │   ├── DisplayArea.Root
    │   ├── TaskDisplayArea
    │   ├── DisplayArea.Tokens (非Task窗口的容器)
    │   └── ImeContainer
    │
    ├── Task (任务,原ActivityStack和TaskRecord)
    │   └── 可以包含子Task
    │
    ├── ActivityRecord (Activity记录)
    │   └── extends WindowToken
    │
    ├── WindowToken (窗口令牌分组)
    │   └── 包含多个WindowState
    │
    └── WindowState (窗口状态)
        └── 可以包含子窗口WindowState
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25

    # 15.2 Z-Order的计算方式

    WindowContainer树中,Z-Order由容器的位置决定:

    // WindowContainer.java
    class WindowContainer<E extends WindowContainer> {
        // 子容器列表,位置越后 = Z-Order越高
        protected final WindowList<E> mChildren = new WindowList<>();
        
        // 添加子容器到指定位置
        void addChild(E child, int index) {
            mChildren.add(index, child);
            child.mParent = this;
            
            // 重新分配Layer
            assignChildLayers(mSyncTransaction);
        }
        
        // 分配子容器的Layer
        void assignChildLayers(SurfaceControl.Transaction t) {
            int layer = 0;
            for (int i = 0; i < mChildren.size(); i++) {
                E child = mChildren.get(i);
                child.assignLayer(t, layer);
                layer++;
                
                // 递归分配子容器的Layer
                child.assignChildLayers(t);
            }
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27

    # 15.3 完整的窗口层级树示例

    实际运行时的WindowContainer树:
    
    RootWindowContainer
    └── DisplayContent (id=0, "Built-in Screen")
        └── DisplayArea.Root ("Root")
            ├── DisplayArea ("Leaf:0:0") [Layer 0]
            │   └── WindowToken (TYPE_WALLPAPER)
            │       └── WindowState ("com.android.wallpaper") [壁纸]
            │
            ├── TaskDisplayArea ("DefaultTaskDisplayArea") [Layer 1~2]
            │   ├── Task #1 (Root Task, fullscreen)
            │   │   └── Task #5 (Leaf Task)
            │   │       ├── ActivityRecord ("com.app.B/.ActivityB")
            │   │       │   └── WindowState ("com.app.B/ActivityB")
            │   │       └── ActivityRecord ("com.app.A/.ActivityA")
            │   │           ├── WindowState ("com.app.A/ActivityA") [主窗口]
            │   │           └── WindowState ("PopupWindow:xxx") [子窗口]
            │   └── Task #2 (Root Task, home)
            │       └── Task #6 (Leaf Task)
            │           └── ActivityRecord ("com.launcher/.Main")
            │               └── WindowState ("com.launcher/Main")
            │
            ├── DisplayArea ("Leaf:12:12") [Layer 12]
            │   └── ImeContainer
            │       └── WindowToken (TYPE_INPUT_METHOD)
            │           └── WindowState ("InputMethod") [输入法]
            │
            ├── DisplayArea ("Leaf:15:15") [Layer 15]
            │   └── WindowToken (TYPE_APPLICATION_OVERLAY)
            │       └── WindowState ("悬浮窗") [应用悬浮窗]
            │
            ├── DisplayArea ("Leaf:17:17") [Layer 17]
            │   └── WindowToken (TYPE_VOLUME_OVERLAY)
            │       └── WindowState ("VolumeDialog") [音量条]
            │
            ├── DisplayArea ("Leaf:20:20") [Layer 20]
            │   └── WindowToken (TYPE_NAVIGATION_BAR)
            │       └── WindowState ("NavigationBar") [导航栏]
            │
            └── DisplayArea ("Leaf:25:25") [Layer 25]
                └── WindowToken (TYPE_STATUS_BAR)
                    └── WindowState ("StatusBar") [状态栏]
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42

    # 16.WMS的性能优化与调试

    # 16.1 WMS相关的性能问题

    WMS层面常见的性能问题:
    ├── 窗口泄漏:Activity销毁但Dialog未dismiss
    │   └── 现象:WindowLeaked异常
    │   └── 原因:Dialog的WindowState没有被移除
    │
    ├── 窗口布局频繁:requestLayout过于频繁
    │   └── 现象:频繁触发WMS布局计算
    │   └── 原因:View频繁变化导致多次relayout
    │
    ├── Surface创建延迟:首帧显示慢
    │   └── 现象:Activity启动白屏时间长
    │   └── 原因:Surface创建和首次绘制耗时
    │
    ├── 动画掉帧:转场动画不流畅
    │   └── 现象:Activity切换卡顿
    │   └── 原因:SurfaceFlinger合成超时或GPU负载过高
    │
    └── 输入延迟:触摸响应慢
        └── 现象:点击响应不及时
        └── 原因:WMS焦点切换延迟或Input分发队列堆积
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

    # 16.2 WMS调试命令

    # 查看所有窗口信息
    adb shell dumpsys window windows
    
    # 查看窗口层级树
    adb shell dumpsys window containers
    
    # 查看焦点窗口
    adb shell dumpsys window displays | grep -E "mCurrentFocus|mFocusedApp"
    
    # 查看输入法状态
    adb shell dumpsys window input
    
    # 查看窗口策略
    adb shell dumpsys window policy
    
    # 查看Surface信息
    adb shell dumpsys SurfaceFlinger
    
    # 查看SurfaceFlinger的Layer树
    adb shell dumpsys SurfaceFlinger --list
    
    # 查看帧率统计
    adb shell dumpsys SurfaceFlinger --latency
    
    # 开启窗口动画调试
    adb shell settings put global window_animation_scale 5.0    # 放慢5倍
    adb shell settings put global transition_animation_scale 5.0
    adb shell settings put global animator_duration_scale 5.0
    
    # 恢复正常
    adb shell settings put global window_animation_scale 1.0
    adb shell settings put global transition_animation_scale 1.0
    adb shell settings put global animator_duration_scale 1.0
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33

    # 16.3 使用Systrace分析WMS

    Systrace中WMS相关的关键Tag:
    
    wm (Window Manager):
      ├── addWindow:窗口添加耗时
      ├── removeWindow:窗口移除耗时
      ├── relayoutWindow:窗口重布局耗时
      ├── performLayout:布局计算耗时
      └── performSurfacePlacement:Surface更新耗时
    
    SurfaceFlinger:
      ├── onMessageReceived:消息处理耗时
      ├── handleMessageInvalidate:处理Buffer更新
      ├── handleMessageRefresh:合成刷新
      │   ├── preComposition:预合成
      │   ├── rebuildLayerStacks:重建Layer栈
      │   ├── setUpHWComposer:配置硬件合成
      │   ├── doComposition:执行合成
      │   └── postComposition:后合成处理
      └── onVsync:VSync信号处理
    
    分析方法:
    1. 在Systrace中找到"wm"和"SurfaceFlinger"的Track
    2. 查看每帧的处理耗时是否超过16.6ms
    3. 找到耗时异常的帧,分析具体哪个操作慢
    4. 常见问题:performLayout耗时(窗口过多或布局复杂)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25

    # 17.总结与技术思考

    # 17.1 核心要点

    WMS核心要点总结:
    
    1. 窗口类型三分:应用窗口、子窗口、系统窗口
       └── 类型决定层级,层级决定显示顺序
    
    2. WindowContainer统一树:Android 10+的核心架构
       └── 从RootWindowContainer到WindowState的完整层级
    
    3. Surface是窗口的绘制载体
       └── 通过BufferQueue与SurfaceFlinger交换数据
       └── SurfaceControl.Transaction实现原子属性修改
    
    4. VSync驱动的显示流水线
       └── 应用绘制→WMS管理→SurfaceFlinger合成→Display显示
    
    5. Insets系统管理系统UI区域
       └── 替代了旧的fitSystemWindows机制
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

    # 17.2 WMS与其他服务的关系

    WMS的服务依赖关系:
    
             ┌──────────────┐
             │     AMS      │  组件生命周期
             │    (ATMS)    │  Activity状态
             └──────┬───────┘
                    │ 共享WindowContainer树
             ┌──────▼───────┐
             │     WMS      │  窗口管理核心
             └──┬──────┬────┘
                │      │
        ┌───────▼──┐ ┌─▼──────────┐
        │  IMS     │ │SurfaceFlngr│
        │(输入管理)│ │  (合成显示)  │
        └──────────┘ └────────────┘
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    # 17.3 学习建议

    学习WMS建议按以下路径:

    1. 先理解窗口类型和层级——知道屏幕上有哪些窗口、为什么按这个顺序排列
    2. 再理解WindowContainer树——知道WMS内部如何组织窗口
    3. 然后理解Surface机制——知道窗口内容如何绘制和显示
    4. 最后理解动画和布局——知道窗口如何变换和适配

    调试技巧:善用adb shell dumpsys window系列命令,可以实时查看WMS的内部状态,是理解WMS的最佳实践途径。


    # 18.Window与DecorView创建过程

    # 18.1 Activity组件UI实现

    Activity组件的UI实现需要与WindowManagerService和SurfaceFlinger服务进行交互:

    • Activity通过Session Binder对象请求WMS创建WindowState对象,描述窗口状态
    • 应用程序通过Client Binder对象请求SurfaceFlinger创建Layer对象,描述窗口数据
    Activity UI实现的完整架构:
    
    Activity
    ├── PhoneWindow (窗口抽象)
    │   ├── DecorView (顶层View)
    │   │   ├── TitleBar/ActionBar
    │   │   └── ContentView (android.R.id.content)
    │   │       └── 开发者的布局
    │   └── WindowManager.LayoutParams (窗口属性)
    │
    ├── WindowManagerImpl → WindowManagerGlobal
    │   └── ViewRootImpl (每个Window一个)
    │       ├── mSurface → Surface (绘图表面)
    │       ├── mView → DecorView
    │       ├── mWindowSession → IWindowSession (WMS代理)
    │       └── InputChannel (事件通道)
    │
    └── 与系统服务的交互:
        App进程                    system_server              SurfaceFlinger
        ViewRootImpl               WMS                        
        │ setView()                │                          │
        │ → addToDisplay() ──→    │ addWindow()              │
        │                          │ → 创建WindowState        │
        │                          │ → 分配Layer层级          │
        │                          │ → createSurfaceControl()│
        │                          │ ──────────────→         │ 创建Layer
        │ ← 返回Surface ────     │                          │
        │ 在Surface上绘制          │                          │
        │ → lockCanvas()           │                          │
        │ → draw()                 │                          │
        │ → unlockAndPost()        │                          │ 合成显示
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31

    # 18.2 PhoneWindow创建过程

    PhoneWindow是在Activity.attach()方法中创建的:

    final void attach(Context context, ActivityThread aThread, ...) {
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
    }
    
    1
    2
    3

    PhoneWindow类有两个重要成员变量:

    • mDecor(DecorView类型):描述窗口视图
    • mContentParent(ViewGroup类型):描述视图内容的父容器

    在attach方法中还会完成以下设置:

    1. setCallback(this):将Activity实现的Callback接口设置到PhoneWindow,使IO输入事件(键盘、触摸屏)可以转发给Activity处理
    2. setSoftInputMode():设置软键盘输入区域的显示模式
    3. setWindowManager():设置应用程序窗口的本地窗口管理器

    # 18.3 DecorView创建过程

    DecorView在PhoneWindow.setContentView()中被创建:

    DecorView创建流程:
    
    Activity.setContentView()
    → PhoneWindow.setContentView()
        → installDecor()          // mContentParent为null时创建
            → generateDecor()    // 创建DecorView
            → generateLayout()   // 根据主题加载布局到DecorView
        → mLayoutInflater.inflate(layoutResID, mContentParent)
          // 将自定义布局添加到content容器中
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    Activity中布局层次:mDecor → mContentRoot → mContentParent → 自定义layoutView

    # 18.4 ViewRootImpl创建过程

    ViewRootImpl是在Activity被激活(handleResumeActivity)时创建的:

    ViewRootImpl创建流程:
    
    ActivityThread.handleResumeActivity()
    → performResumeActivity()    // 触发Activity.onResume()
    → r.activity.makeVisible()
        → wm.addView(mDecor, ...)
    → WindowManagerImpl.addView()
    → WindowManagerGlobal.addView()
        ├── new ViewRootImpl()   // 创建ViewRootImpl
        ├── mViews.add(view)     // 保存DecorView
        ├── mRoots.add(root)     // 保存ViewRootImpl
        └── root.setView(view, ...)
            └── requestLayout()  // 第一次布局请求
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    ViewRootImpl的核心职责:

    1. 负责为应用程序窗口视图创建Surface
    2. 配合WMS管理系统的应用程序窗口
    3. 负责管理、布局和渲染应用程序窗口视图的UI
    4. 接收Activity组件的IO输入事件(键盘事件等)

    # 18.5 Dialog中Window创建

    Dialog的Window创建过程与Activity类似:

    1. 创建Window:通过PolicyManager的makeNewWindow方法完成
    2. 初始化DecorView:通过Window.setContentView()实现
    3. 显示:在Dialog.show()方法中,通过WindowManager将DecorView添加到Window中
    4. 关闭:通过WindowManager.removeViewImmediate(mDecor)移除DecorView

    重要限制:Dialog必须使用Activity的Context,因为需要应用token。Application和Service的Context没有token,会导致窗口添加失败。如果确需使用,可将Dialog的Window类型设置为系统Window(TYPE_SYSTEM_ERROR等)。

    # 18.6 四者关系总结

    Activity、PhoneWindow、DecorView、ViewRootImpl 关系:
    
    Activity
    └── PhoneWindow (mWindow)
        └── DecorView (mDecor)
            ├── LinearLayout
            │   ├── ViewStub (ActionBar)
            │   ├── FrameLayout (titleBar)
            │   └── FrameLayout (id=content) ← setContentView的内容
            └── 关联 ViewRootImpl
                ├── 管理View的绘制(measure/layout/draw)
                ├── 管理IO输入事件
                └── 通过Surface与SurfaceFlinger通信
    
    创建时机:
    PhoneWindow → Activity.attach()
    DecorView → Activity.onCreate() → setContentView()
    ViewRootImpl → Activity.onResume() → makeVisible() → addView()
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    上次更新: 2026/06/10, 11:13:41
    自定义View设计
    PMS与APK安装

    ← 自定义View设计 PMS与APK安装→

    最近更新
    01
    信号崩溃快速排查
    06-15
    02
    CoreDump破案
    06-15
    03
    perf火焰图实战
    06-15
    更多文章>
    Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式