11、WindowManagerService 启动与初始化:WMS 的创建时机、systemReady() 回调、DisplayContent 与 DisplayManager 的关联

各位同学,今天我们来聊聊 WMS 的启动与初始化。说实话,这部分内容在源码里藏得挺深,很多同学看了好几遍源码,还是搞不清 WMS 到底是在什么时候创建的。我当年刚接触 Android 系统时,也在这个地方绕了很久。

WMS 的启动,说白了就是 SystemServer 进程里的一连串调用。它不像 Activity 那样有个显眼的 onCreate,而是悄无声息地嵌在系统启动流程中。嗯,这里我们要抓住三个关键点:创建时机、systemReady 回调、以及 DisplayContent 与 DisplayManager 的关联。

11.1 WMS 的创建时机

WMS 是在 SystemServer 的 startOtherServices() 方法中被创建的。你想想看,SystemServer 启动了几十个服务,WMS 只是其中之一。但它的位置很特殊——它依赖于 InputManagerService、PowerManagerService 等服务的就绪。

我习惯把 WMS 的创建过程分为三个阶段:

  1. 实例化阶段:在 SystemServer 中调用 WindowManagerService.main()
  2. 初始化阶段:在 WMS 构造函数中完成内部数据结构的初始化
  3. 就绪阶段:通过 systemReady() 回调完成最终准备

来看一段核心代码,这是 WMS 的创建入口:

// frameworks/base/services/java/com/android/server/SystemServer.java
private void startOtherServices() {
    // ... 前面一堆服务的启动
    
    // 创建 WMS 实例
    wm = WindowManagerService.main(context, inputManager,
            mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
            !mFirstBoot, mOnlyCore, new PhoneWindowManager());
    
    // 注册到 ServiceManager
    ServiceManager.addService(Context.WINDOW_SERVICE, wm);
    
    // ... 后续服务的启动
}

这里有个细节要注意:WindowManagerService.main() 是一个静态方法,它内部会 new 一个 WMS 实例。我在项目中遇到过一个问题,就是 WMS 创建时如果 InputManager 还没准备好,会导致空指针。后来看了源码才发现,WMS 构造函数里会主动检查 InputManager 的状态。

关键点:WMS 的创建不是一步到位的。构造函数里只做了基础初始化,真正的「启动」要等到 systemReady() 被调用。

11.2 systemReady() 回调

systemReady() 是 WMS 初始化的第二幕。它是在 SystemServer 的 systemReady() 阶段被调用的。说白了,这时候系统的大部分服务都已经就绪了,WMS 可以开始做一些「需要依赖其他服务」的事情了。

WMS 的 systemReady() 主要做了以下几件事:

  • 初始化 DisplayContent 集合
  • 与 DisplayManagerService 建立关联
  • 恢复之前保存的窗口状态(如果有)
  • 启动窗口动画系统

我曾经踩过一个坑:在 systemReady() 之前尝试获取 Display 信息,结果返回的是 null。这是因为 DisplayManagerService 还没完成初始化,Display 的信息还没注册到系统中。所以,如果你在开发系统应用时,一定要等到 systemReady() 之后再去操作 Display。

// frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public void systemReady() {
    // 确保 DisplayManager 已经就绪
    mDisplayManager = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
    
    // 初始化 DisplayContent
    mRoot.createDisplayContent(mDisplayManager);
    
    // 恢复窗口状态
    restoreWindowState();
    
    // 启动动画
    mAnimator.systemReady();
}

个人经验:systemReady() 是 WMS 初始化的分水岭。在这之前,WMS 只能做「独立」的初始化工作;在这之后,WMS 才能与 DisplayManager、InputManager 等兄弟服务协同工作。

11.3 DisplayContent 与 DisplayManager 的关联

这部分是 WMS 初始化的核心。DisplayContent 是什么?它代表了一个逻辑显示屏。在 Android 系统中,一个设备可能有多个显示屏(比如手机屏幕 + 电视投屏),每个显示屏对应一个 DisplayContent。

DisplayManager 则是管理所有物理显示器的系统服务。WMS 需要从 DisplayManager 那里获取显示器的信息,然后创建对应的 DisplayContent。

我画了一张图,帮你理清它们之间的关系:

WMS 初始化核心架构图 SystemServer startOtherServices() WindowManagerService main() → 构造函数 创建 systemReady() 初始化第二阶段 回调 DisplayManager 管理物理显示器 DisplayContent 逻辑显示屏 获取显示器信息 创建 RootWindowContainer 管理所有 DisplayContent 注册 归属 核心流程:SystemServer → WMS.main() → systemReady() → DisplayManager → DisplayContent → RootWindowContainer 系统服务 WMS 回调 核心数据结构

从图中你可以看到,WMS 的初始化是一个链式过程。SystemServer 创建 WMS 后,WMS 在 systemReady() 中与 DisplayManager 交互,获取显示器信息,然后创建 DisplayContent,最后注册到 RootWindowContainer 中统一管理。

这里有个重要的数据结构——RootWindowContainer。它是所有 DisplayContent 的容器,也是 WMS 管理窗口的顶层入口。我习惯把它理解为「窗口世界的上帝」,所有窗口的添加、删除、布局都要经过它。

避坑指南:我曾经在 systemReady() 之前尝试通过 WMS 获取 Display 信息,结果返回的是空列表。这是因为 DisplayContent 的创建依赖于 DisplayManager 的就绪状态。所以,如果你在开发系统级功能时,一定要等到 systemReady() 回调之后再去操作 Display 相关的 API。

11.4 总结

WMS 的启动与初始化,说白了就是三个步骤:

阶段 触发时机 核心工作
创建 SystemServer.startOtherServices() 实例化 WMS,注册到 ServiceManager
就绪 SystemServer.systemReady() 初始化 DisplayContent,关联 DisplayManager
运行 系统启动完成 处理窗口操作,管理显示区域

嗯,这里要注意的是,WMS 的初始化不是一蹴而就的。它像是一个「延迟初始化」的过程——先创建骨架,再填充血肉。这种设计在 Android 系统中很常见,目的是为了减少启动时间,同时避免服务间的循环依赖。

我个人觉得,理解 WMS 的初始化流程,是掌握整个窗口系统的钥匙。你想想看,如果连 WMS 什么时候创建、什么时候就绪都搞不清楚,后面分析窗口添加、布局、动画这些流程时,肯定会一头雾水。

核心要点:WMS 的初始化分为两个阶段——构造函数中的基础初始化和 systemReady() 中的完整初始化。DisplayContent 的创建是连接 WMS 与 DisplayManager 的关键桥梁。


公众号:蓝海资料掘金营,微信deep3321