Android-Framework-06-WMS-02-WMS

478 阅读14分钟

1 WMS主要作用、启动过程

1.1 WMS职责

image.png

image.png

整个界面就像由N个演员参与的话剧:ViewRoot就是各个演员的长相和表情,取 决于它们各自的条件与努力。WMS就是导演,它要负责话剧的舞台效果、演员站位; SurfaceFling是摄像机,它只负责客观的捕捉当前的画面,然后真实的呈现给观众;可见,WMS与SurfaceFling的一个重要区别就是——后者只做与“显示”相关的事情,而WMS要处理对输入事件的派发。

WindowManagerService (WMS) 是 Android 系统中负责管理窗口的核心服务,负责处理应用窗口的显示、层级管理、动画和用户交互等。以下是 WindowManagerService 的主要职责:


1. 窗口的创建与管理

  • 窗口的添加与删除

    • 负责管理应用程序或系统进程请求的窗口(Window)。
    • 处理窗口的创建(addWindow())、删除(removeWindow())、更新(updateWindow())。

添加: image.png

删除: image.png

  • 窗口层级管理

    • 按照窗口类型和优先级(Z-Order),维护窗口的显示层级。
    • 系统窗口(如通知栏、对话框)通常优先级高于应用窗口。
  • 窗口属性管理

    • 管理窗口的大小、位置、透明度、动画等属性。
    • 支持全屏、分屏、多窗口模式。

2. 窗口与 Surface 的关联

  • 窗口与 Surface 的桥梁

    • 为每个窗口创建一个对应的 Surface,该 Surface 是窗口内容在显示系统中的表现形式。
    • 通过 SurfaceFlinger 合成所有窗口的 Surface,最终显示在屏幕上。
  • 与应用进程通信

    • 为窗口提供 Surface 的句柄,应用通过该句柄绘制内容。

画布:

image.png

在Android系统中每个Activity都有一个独立的画布(在应用侧称为Surface,在SurfaceFlinger侧称为Layer), 无论这个Activity安排了多么复杂的view结构,它们最终都是被画在了所属Activity的这块画布上。在 ViewRootImpl 创建时同时会 new 一个 Surface 对象

private final Surface mSurface = new Surface();

image.png

ViewRootImpl:

frameworks/base/core/java/android/view/ViewRootImpl.java

private void performTraversals() {

......

relayoutResult = relayoutWindow(params, viewVisibility,

insetsPending);

......

}

relayoutWindow函数里会调用到WindowSession的relayout函数,这个函数是一个跨进程调用,mWindowSession可以看作是WMS在应用侧的代表随着代码的执行让我们把视角切换到system_server进程(WMS的relayoutWindow函数),这里会调用createSurfaceControl去创建一个SurfaceControl.

SurfaceControl的创建过程,注意这里创建工作是调用winAnimator来完成的, surfaceController.getSurfaceControl会把创建出来的SurfaceControl通过形参outSurfaceControl传出去

WindowSurfaceController(String name, int w, int h, int format,
                        int flags, WindowStateAnimator animator, int windowType, int ownerUid) {
......
    final SurfaceControl.Builder b = win.makeSurface()
            .setParent(win.getSurfaceControl())
            .setName(name)
            .setBufferSize(w, h)
            .setFormat(format)
            .setFlags(flags)
            .setMetadata(METADATA_WINDOW_TYPE, windowType)
            .setMetadata(METADATA_OWNER_UID, ownerUid)
            .setCallsite("WindowSurfaceController");
......
}

Layer创建:

image.png

image.png

接口操作:

Surface.java(frameworks\base\core\java\android\view)
public class Surface implements Parcelable {
......
    // dequeueBuffer Buffer从BufferQueue出队
    private static native long nativeLockCanvas(long nativeObject, Canvas canvas,
                                                Rect dirty)
            throws OutOfResourcesException;
    // queueBuffer Buffer从BufferQueue入队
    private static native void nativeUnlockCanvasAndPost(long nativeObject, Canvas
            canvas);
......
}

image.png

到这里我们看到java层Surface的lock方法最终它有去调用到GraphicBufferProducer的dequeueBuffer函数,dequeueBuffer在获取一个Slot后。

如果Slot没有分配GraphicBuffer会在这时给它分配GraphicBuffer, 然后会返回一个带有BUFFER_NEEDS_REALLOCATION标记的flag, 应用侧看到这个flag后会通过requestBuffer和importBuffer接口把GraphicBuffer映射到自已的进程空间。


3. 窗口显示和更新

image.png

  • 窗口布局与位置计算

    • 根据窗口的属性(如 gravitylayoutParams),计算窗口在屏幕中的位置。
    • 处理屏幕旋转、大小调整等导致的布局变更。
  • 屏幕刷新协调

    • 当窗口内容或属性发生变化时,通知 SurfaceFlinger 更新屏幕显示。

4. 输入事件的分发

Android 系统是由事件驱动的,而input 是最常见的事件之一,用户的点击、滑动、长按等操作,都属于input 事件驱动,其中的核心就是InputReader 和 InputDispatcher。InputReader 和 InputDispatcher是跑在SystemServer进程中的两个native 循环线程,负责读取和分发Input 事件。整个处理过程大致流程如下:

image.png

InputReader负责从EventHub里面把Input事件读取出来,然后交给 InputDispatcher 进行事件分发;

InputDispatcher在拿到 InputReader获取的事件之后,对事件进行包装后,寻找并分发到目标窗口;

  • EventHub

使用inotify监听输入设备的添加和移除。

使用epoll机制监听输入设备的数据变化。

读取设备文件的数据。

将原始数据(生事件)返回给InputReader。

  • InputReader

image.png

读取IMS提供的配置信息,比如键盘布局。根据IMS提供的配置信息(包括键盘布局,显示屏信息)对原始事件实施一次转换。将多个事件组合成一个可供上层消费的事件(比如将一组触摸屏的原始事件合并成一个ACTION_DOWN事件) 。

  • InputDispatcher

image.png

根据IMS提供的派发前策略过滤和拦截事件(比如HOME键)

对于按键事件产生模拟按下重复事件,开始重复延迟是500ms,重复的间隔是50ms。

WMS会将当前的所有窗口和窗口信息传给InputDispatcher,以供InputDispatcher寻找派发窗口,找到当前可以接收事件的窗口(比如key事件寻找focus的窗口,motion事件寻找包含这个事件坐标的窗口)将事件派发给窗口

  • InputManager

image.png

  • InputEventReceiver

image.png 包装了InputChannel,负责将InputChannel的FD加入到main looper并负责读写InputChannel。将事件封装成Java层的事件对象向上派发给ViewRootImpl。

image.png

  • InputStage

image.png

image.png

InputStage主要是用来将事件的处理分成若干个阶段(stage)进行,事件依次经过每一个stage,如果该事件没有被处理(标识为FLAG_FINISHED),则该stage就会调用onProcess方法处理,然后调用forward执行下一个stage的处理;如果该事件被标识为处理则直接调用forward,执行下一个stage的处理,直到没有下一个stage。

image.png

  • 事件分发

image.png

image.png

DecorView虽然是ViewGroup,但它并不会直接分发给它的child,也不会传递给它的父View,而是先分发给Activity,为什么要这么做呢?

image.png

我们可想DecorView 拿到事件后,若直接向下分发,那就没Activity的事了,但Activity也想成为事件分发的一环,并还想作为优先级较高的部分。所以就可以通过这种方式,绕了一圈又回到DecorView并再往下分发。DecorView在这里承担两个职责:

  1. 在接收到输入事件时,它不同于普通View,分发给子view,而是会先将事件分发给上层的Activity
  2. 接收到上层Activity分发下的事件后,又会变成普通View,进行ViewGroup的事件分发

5. 窗口动画管理

  • 启动与过渡动画

    • 负责窗口的显示动画、隐藏动画,以及窗口之间的过渡动画(如 Activity 切换)。
  • 动画效果实现

    • 提供窗口动画的支持(通过 SurfaceAnimatorAnimator),提升用户体验。

6. 多窗口与分屏支持

  • 多窗口模式

    • 支持 Android 的多窗口功能,包括自由窗口模式和分屏模式。
  • 窗口栈与任务管理

    • 管理窗口栈(Window Stack)和任务栈(Task Stack),确保多窗口和多任务环境下的正确显示与交互。

7. 屏幕显示与截图

  • 屏幕旋转管理

    • 根据设备方向传感器的状态,调整屏幕旋转。
  • 截图功能

    • 提供截取某个窗口或整个屏幕的能力(如任务管理器预览图)。

8. 与其他系统服务协作

  • ActivityManagerService (AMS)

    • 配合 AMS 管理窗口与 Activity 的生命周期。
    • 当 Activity 状态变化时(如启动、销毁),WMS 会创建或移除对应的窗口。
  • InputManagerService (IMS)

    • 协作处理输入事件,决定事件分发的窗口目标。
  • SurfaceFlinger

    • 提供窗口内容的 Surface,并协调内容提交和显示。
  • DisplayManagerService (DMS)

    • 管理多显示屏(如外接屏幕)的窗口显示。

9. 配置变更处理

  • 屏幕分辨率调整

    • 当设备分辨率、DPI、屏幕方向等配置发生变化时,重新布局和刷新窗口。
  • 多显示屏支持

    • 在多屏环境下,管理窗口在不同屏幕上的分配与显示。

10. 资源管理与性能优化

  • 内存与资源管理

    • 回收未使用的窗口资源,防止内存泄漏。
  • 性能优化

    • 优化窗口更新的频率,避免不必要的屏幕刷新。
    • 支持硬件加速,提升窗口的绘制性能。

1.2 WMS属性

1.mPolicy:WindowManagerPolicy  是窗口管理策略的接口类,用来定义一个窗口策略所要遵循的通用规范,并提供了WindowManager所有的特定的UI行为

2.mSessions:ArraySet  主要用于进程间通信,其他的应用程序进程想要和WMS进程进行通信就需要经过Session,并且每个应用程序进程都会对应一个Session

3.mWindowMap:WindowHashMap  用来保存WMS中各种窗口的集合

4.mResizingWindows:ArrayList  用来存储正在调整大小的窗口的列表

6.mAnimator:WindowAnimator  用于管理窗口的动画以及特效动画

7.mH:H  用来将任务加入到主线程的消息队列中

8.mInputManager:InputManagerService  输入系统的管理者

1.3 WindowState

image.png

WindowState 是 Android 系统中 WindowManagerService(WMS)用来管理和描述每个窗口的核心对象。它是对一个窗口的抽象表示,包含窗口的各种属性和状态信息,并与窗口在屏幕上的显示和交互相关联。

以下是 WindowState 的功能和职责详解:


1. 基本功能

  • 窗口的状态描述

    • WindowState 记录了窗口的大小、位置、层级、透明度、焦点状态等信息。
  • 窗口与应用的桥梁

    • 维护了窗口与应用进程的关联,通过 IBinder 对象连接到应用窗口。

2. 与 WMS 的关系

  • 窗口管理的基础单元

    • WindowState 是 WMS 的核心组成部分,WMS 通过 WindowState 管理和操控所有窗口。
  • 窗口的生命周期管理

    • WindowState 表示窗口从创建到销毁的整个生命周期。

3. WindowState 的组成

WindowState 的内部数据结构比较复杂,主要由以下几个关键部分组成:

(1)基本信息
  • 窗口属性(WindowManager.LayoutParams)

    • 包括窗口类型(Type)、窗口尺寸(Width/Height)、位置(X/Y)、是否全屏等信息。
    • 定义了窗口的行为,例如是否响应触摸事件、是否可见等。
  • 窗口的层级(Z-Order)

    • 根据窗口类型和层级规则,决定窗口在屏幕上显示的顺序。
(2)状态信息
  • 焦点状态

    • 是否为当前活动窗口(焦点窗口)。
  • 可见性状态

    • 窗口是否可见,以及窗口的透明度(Alpha)。
  • 动画状态

    • 窗口是否正在播放动画(如打开、关闭过渡动画)。
(3)关联组件
  • 应用进程关联

    • 包含与应用的通信通道,通过 IBinder 连接。
  • SurfaceControl

    • SurfaceFlinger 通信的关键对象,用于实际的窗口显示。
  • InputChannel

    • InputManagerService 的通信通道,用于接收输入事件(如触摸和按键)。

4. WindowState 的职责

WindowState 作为窗口的核心对象,承担了多种职责:

(1)窗口的添加与删除
  • 在窗口被添加到 WMS 时,创建一个对应的 WindowState 对象。
  • 当窗口被移除时,销毁其对应的 WindowState。
(2)窗口属性的管理
  • 维护和更新窗口的各种属性,包括大小、位置、层级、透明度等。
(3)窗口显示管理
  • 确定窗口在屏幕上的显示区域。
  • 根据窗口的可见性状态(Visible/InVisible),通知 SurfaceFlinger 更新显示内容。
(4)输入事件的管理
  • 管理窗口是否接受输入事件。
  • 确定窗口的触摸区域(TouchableRegion)和焦点。
(5)窗口动画支持
  • 管理窗口的显示和隐藏动画。
  • SurfaceAnimator 协作,实现过渡效果。

5. WindowState 的关键方法

WindowState 提供了许多方法,用于支持窗口的各种操作:

(1)显示管理相关
  • show()

    • 显示窗口。
  • hide()

    • 隐藏窗口。
  • isVisible()

    • 判断窗口是否可见。
(2)位置和尺寸管理
  • setFrame()

    • 设置窗口的显示区域。
  • getBounds()

    • 获取窗口的边界信息。
(3)输入事件相关
  • canReceiveKeys()

    • 判断窗口是否能够接收按键事件。
  • getTouchableRegion()

    • 获取窗口的触摸区域。
(4)动画相关
  • applyAnimation()

    • 为窗口应用动画效果。
  • cancelAnimation()

    • 取消当前窗口的动画。

6. WindowState 的关联对象

WindowState 通常会与以下对象紧密关联:

(1)Session
  • 每个应用的窗口都通过一个 Session 对象与 WMS 通信。
  • Session 负责管理应用窗口的生命周期。
(2)SurfaceControl
  • WindowState 通过 SurfaceControlSurfaceFlinger 交互,负责窗口内容的绘制和合成。
(3)InputChannel
  • 窗口通过 InputChannel 接收来自 InputManagerService 的输入事件。
(4)WindowToken
  • 每个窗口都对应一个 WindowToken,用于标识窗口所属的任务或应用。
(5)DisplayContent

如果说WindowToken按照窗口之间的逻辑关系将其分组,那么DisplayContent则根据窗口的显示位置将其分组。隶属于同一个DisplayContent的窗口将会被显示在同一个屏幕中。每一个DisplayContent都对应这一个唯一的ID,在添加窗口时可以通过指定这个ID决定其将被显示在那个屏幕中。

DisplayContent是一个非常具有隔离性的一个概念。处于不同DisplayContent的两个窗口在布局、显示顺序以及动画处理上不会产生任何耦合。因此,就这几个方面来说, DisplayContent就像一个孤岛,所有这些操作都可以在其内部独立执行。因此,这些本来属于整个WMS全局性的操作,变成了DisplayContent内部的操作了。

2 WMS启动过程

image.png

WindowManagerService (WMS) 是 Android 系统中负责窗口管理的核心服务,它的启动过程伴随系统启动,并由 SystemServer 完成初始化。以下是 WMS 的完整启动过程解析。


1. 启动入口

SystemServer 入口

  • WMS 的启动由 SystemServerstartOtherServices() 方法触发。
  • 代码位置:com.android.server.SystemServer
java
复制代码
private void startOtherServices() {
    ...
    // 启动 WMS 服务
    wm = WindowManagerService.main(context, inputManager, wmHandler, displayThread, surfaceAnimationThread);
    ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ true);
    ...
}

2. WindowManagerService 初始化

WindowManagerService.main()

  • WMS 的 main() 方法是启动的入口。
  • 此方法负责实例化 WMS 并完成基础配置。
java
复制代码
public static WindowManagerService main(Context context, InputManagerService inputManager,
        Handler handler, DisplayThread displayThread, SurfaceAnimationThread surfaceAnimationThread) {
    WindowManagerService wms = new WindowManagerService(context, inputManager, handler,
            displayThread, surfaceAnimationThread);
    wms.onInitReady();
    return wms;
}

WindowManagerService 构造方法

  • 构造方法中完成核心组件的初始化,包括以下内容:

    • InputManagerService:用于处理输入事件。
    • DisplayManagerService:管理显示设备(屏幕)和显示内容。
    • WindowHashMap:存储当前所有的窗口信息。
    • Policy 初始化:通过 WindowManagerPolicy 提供窗口管理的行为策略(如导航栏、状态栏的处理)。
java
复制代码
public WindowManagerService(Context context, InputManagerService inputManager, ...) {
    ...
    mInputManager = inputManager; // 输入管理服务
    mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE); // 显示服务
    mPolicy = new PhoneWindowManager(); // 窗口管理策略
    ...
}

3. WMS 线程初始化

WMS 运行在独立的线程中,完成消息处理和主循环初始化。

核心线程

  • WMS 需要多个线程来协作完成任务:

    • UI 线程:主线程用于管理窗口更新。
    • 输入事件线程:处理触摸、键盘等输入。
    • 动画线程:专门用于窗口动画的执行。
java
复制代码
mH = new H(Looper.myLooper()); // 处理 WMS 内部消息
mAnimationHandler = new Handler(surfaceAnimationThread.getLooper());

4. 核心子组件的初始化

DisplayContent 初始化

  • DisplayContent 是 WMS 管理每个屏幕显示的核心对象。
  • 在系统启动时,会为主屏幕创建一个 DisplayContent。
java
复制代码
DisplayContent displayContent = mRoot.getDisplayContent(DEFAULT_DISPLAY);
if (displayContent == null) {
    displayContent = new DisplayContent(DEFAULT_DISPLAY, this);
    mRoot.addChild(displayContent);
}

WindowToken 和 RootWindowContainer

  • WindowToken:每个窗口的标识,用于管理窗口生命周期。
  • RootWindowContainer:WMS 的根容器,管理所有窗口和显示内容。

5. 输入系统绑定

WMS 必须与 InputManagerService 绑定,完成输入事件的接收和分发。

InputChannel 注册

  • 创建并注册输入通道,用于接收触摸事件。
  • 将窗口的触摸区域和焦点信息传递给 InputManagerService
java
复制代码
mInputManager.setInputWindows(mInputMonitor.updateInputWindowsLw());

6. DisplayManager 绑定

WMS 与 DisplayManagerService 绑定,以确保能够正确管理显示设备。

注册显示监听器

  • 通过监听 DisplayManager 的变化,动态适配多屏幕显示。
java
复制代码
mDisplayManager.registerDisplayListener(new DisplayListener());

7. 完成启动

onInitReady()

  • onInitReady() 在 WMS 启动完成后调用,用于启动后续依赖服务。
java
复制代码
public void onInitReady() {
    mPolicy.setInitialDisplaySize();
    mPolicy.init(); // 初始化窗口策略
}

向 ServiceManager 注册

  • 最后,WMS 注册到系统中,其他组件可以通过 Context.WINDOW_SERVICE 获取它。
java
复制代码
ServiceManager.addService(Context.WINDOW_SERVICE, this);

8. 结论:启动流程简化图

  1. SystemServer 启动

    • 调用 WindowManagerService.main(),创建并初始化 WMS。
  2. 核心组件初始化

    • 构造方法中初始化输入、显示、策略和线程。
  3. 绑定子系统

    • 与 InputManager 和 DisplayManager 完成绑定。
  4. 完成注册

    • 将 WMS 注册到 ServiceManager,完成启动。