1 WMS主要作用、启动过程
1.1 WMS职责
整个界面就像由N个演员参与的话剧:ViewRoot就是各个演员的长相和表情,取 决于它们各自的条件与努力。WMS就是导演,它要负责话剧的舞台效果、演员站位; SurfaceFling是摄像机,它只负责客观的捕捉当前的画面,然后真实的呈现给观众;可见,WMS与SurfaceFling的一个重要区别就是——后者只做与“显示”相关的事情,而WMS要处理对输入事件的派发。
WindowManagerService (WMS) 是 Android 系统中负责管理窗口的核心服务,负责处理应用窗口的显示、层级管理、动画和用户交互等。以下是 WindowManagerService 的主要职责:
1. 窗口的创建与管理
-
窗口的添加与删除:
- 负责管理应用程序或系统进程请求的窗口(
Window
)。 - 处理窗口的创建(
addWindow()
)、删除(removeWindow()
)、更新(updateWindow()
)。
- 负责管理应用程序或系统进程请求的窗口(
添加:
删除:
-
窗口层级管理:
- 按照窗口类型和优先级(Z-Order),维护窗口的显示层级。
- 系统窗口(如通知栏、对话框)通常优先级高于应用窗口。
-
窗口属性管理:
- 管理窗口的大小、位置、透明度、动画等属性。
- 支持全屏、分屏、多窗口模式。
2. 窗口与 Surface 的关联
-
窗口与 Surface 的桥梁:
- 为每个窗口创建一个对应的
Surface
,该Surface
是窗口内容在显示系统中的表现形式。 - 通过
SurfaceFlinger
合成所有窗口的Surface
,最终显示在屏幕上。
- 为每个窗口创建一个对应的
-
与应用进程通信:
- 为窗口提供
Surface
的句柄,应用通过该句柄绘制内容。
- 为窗口提供
画布:
在Android系统中每个Activity都有一个独立的画布(在应用侧称为Surface,在SurfaceFlinger侧称为Layer), 无论这个Activity安排了多么复杂的view结构,它们最终都是被画在了所属Activity的这块画布上。在 ViewRootImpl 创建时同时会 new 一个 Surface 对象
private final Surface mSurface = new Surface();
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创建:
接口操作:
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);
......
}
到这里我们看到java层Surface的lock方法最终它有去调用到GraphicBufferProducer的dequeueBuffer函数,dequeueBuffer在获取一个Slot后。
如果Slot没有分配GraphicBuffer会在这时给它分配GraphicBuffer, 然后会返回一个带有BUFFER_NEEDS_REALLOCATION标记的flag, 应用侧看到这个flag后会通过requestBuffer和importBuffer接口把GraphicBuffer映射到自已的进程空间。
3. 窗口显示和更新
-
窗口布局与位置计算:
- 根据窗口的属性(如
gravity
和layoutParams
),计算窗口在屏幕中的位置。 - 处理屏幕旋转、大小调整等导致的布局变更。
- 根据窗口的属性(如
-
屏幕刷新协调:
- 当窗口内容或属性发生变化时,通知
SurfaceFlinger
更新屏幕显示。
- 当窗口内容或属性发生变化时,通知
4. 输入事件的分发
Android 系统是由事件驱动的,而input 是最常见的事件之一,用户的点击、滑动、长按等操作,都属于input 事件驱动,其中的核心就是InputReader 和 InputDispatcher。InputReader 和 InputDispatcher是跑在SystemServer进程中的两个native 循环线程,负责读取和分发Input 事件。整个处理过程大致流程如下:
InputReader负责从EventHub里面把Input事件读取出来,然后交给 InputDispatcher 进行事件分发;
InputDispatcher在拿到 InputReader获取的事件之后,对事件进行包装后,寻找并分发到目标窗口;
- EventHub
使用inotify监听输入设备的添加和移除。
使用epoll机制监听输入设备的数据变化。
读取设备文件的数据。
将原始数据(生事件)返回给InputReader。
- InputReader
读取IMS提供的配置信息,比如键盘布局。根据IMS提供的配置信息(包括键盘布局,显示屏信息)对原始事件实施一次转换。将多个事件组合成一个可供上层消费的事件(比如将一组触摸屏的原始事件合并成一个ACTION_DOWN事件) 。
- InputDispatcher
根据IMS提供的派发前策略过滤和拦截事件(比如HOME键)
对于按键事件产生模拟按下重复事件,开始重复延迟是500ms,重复的间隔是50ms。
WMS会将当前的所有窗口和窗口信息传给InputDispatcher,以供InputDispatcher寻找派发窗口,找到当前可以接收事件的窗口(比如key事件寻找focus的窗口,motion事件寻找包含这个事件坐标的窗口)将事件派发给窗口
- InputManager
- InputEventReceiver
包装了InputChannel,负责将InputChannel的FD加入到main looper并负责读写InputChannel。将事件封装成Java层的事件对象向上派发给ViewRootImpl。
- InputStage
InputStage主要是用来将事件的处理分成若干个阶段(stage)进行,事件依次经过每一个stage,如果该事件没有被处理(标识为FLAG_FINISHED),则该stage就会调用onProcess方法处理,然后调用forward执行下一个stage的处理;如果该事件被标识为处理则直接调用forward,执行下一个stage的处理,直到没有下一个stage。
- 事件分发
DecorView虽然是ViewGroup,但它并不会直接分发给它的child,也不会传递给它的父View,而是先分发给Activity,为什么要这么做呢?
我们可想DecorView 拿到事件后,若直接向下分发,那就没Activity的事了,但Activity也想成为事件分发的一环,并还想作为优先级较高的部分。所以就可以通过这种方式,绕了一圈又回到DecorView并再往下分发。DecorView在这里承担两个职责:
- 在接收到输入事件时,它不同于普通View,分发给子view,而是会先将事件分发给上层的Activity
- 接收到上层Activity分发下的事件后,又会变成普通View,进行ViewGroup的事件分发
5. 窗口动画管理
-
启动与过渡动画:
- 负责窗口的显示动画、隐藏动画,以及窗口之间的过渡动画(如 Activity 切换)。
-
动画效果实现:
- 提供窗口动画的支持(通过
SurfaceAnimator
和Animator
),提升用户体验。
- 提供窗口动画的支持(通过
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
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 通过
SurfaceControl
与SurfaceFlinger
交互,负责窗口内容的绘制和合成。
(3)InputChannel
- 窗口通过
InputChannel
接收来自 InputManagerService 的输入事件。
(4)WindowToken
- 每个窗口都对应一个
WindowToken
,用于标识窗口所属的任务或应用。
(5)DisplayContent
如果说WindowToken按照窗口之间的逻辑关系将其分组,那么DisplayContent则根据窗口的显示位置将其分组。隶属于同一个DisplayContent的窗口将会被显示在同一个屏幕中。每一个DisplayContent都对应这一个唯一的ID,在添加窗口时可以通过指定这个ID决定其将被显示在那个屏幕中。
DisplayContent是一个非常具有隔离性的一个概念。处于不同DisplayContent的两个窗口在布局、显示顺序以及动画处理上不会产生任何耦合。因此,就这几个方面来说, DisplayContent就像一个孤岛,所有这些操作都可以在其内部独立执行。因此,这些本来属于整个WMS全局性的操作,变成了DisplayContent内部的操作了。
2 WMS启动过程
WindowManagerService (WMS) 是 Android 系统中负责窗口管理的核心服务,它的启动过程伴随系统启动,并由 SystemServer 完成初始化。以下是 WMS 的完整启动过程解析。
1. 启动入口
SystemServer 入口
- WMS 的启动由 SystemServer 的
startOtherServices()
方法触发。 - 代码位置:
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. 结论:启动流程简化图
-
SystemServer 启动:
- 调用
WindowManagerService.main()
,创建并初始化 WMS。
- 调用
-
核心组件初始化:
- 构造方法中初始化输入、显示、策略和线程。
-
绑定子系统:
- 与 InputManager 和 DisplayManager 完成绑定。
-
完成注册:
- 将 WMS 注册到
ServiceManager
,完成启动。
- 将 WMS 注册到