WMS(Window Manager Service)是 Android 系统中的一个系统服务,它是WindowManager的管理者,负责对窗口进行管理、分配资源,以及处理用户的输入事件等问题。WMS是整个系统中非常重要的一个组成部分,可以说,没有 WMS,Android 系统就无法正常运行。
WMS 的职责主要包括以下几个方面:
窗口管理
WMS 负责对窗口的启动、添加和删除进行管理。它会跟踪每一个窗口的状态,并根据需要调整它们的大小和位置。此外,WMS 还负责窗口的层级关系,即哪个窗口在最上层或最下层。
窗口动画
WMS 的动画子系统处理窗口之间的切换效果,例如窗口打开和关闭时的动画效果,以及多个窗口同时出现时的交互效果等。这些动画效果不仅可以美化界面,还可以增强用户体验。
输入系统中转站
WMS 是输入系统的中转站,它会将用户输入的事件派发到最合适的窗口中。例如,当用户点击屏幕时,WMS 会根据焦点窗口的位置和大小等信息,决定哪个窗口应该接受这个事件。
Surface 管理
Surface 是 Android 系统中的一个重要概念,它负责窗口的绘制和显示。WMS 负责管理每个窗口的 Surface,确保每个窗口都有一个可用的 Surface,并根据需要进行分配和释放。
窗口焦点管理
WMS 还负责管理窗口的焦点。当用户与屏幕交互时,WMS 会决定哪个窗口可以接收输入事件,并将焦点从一个窗口转移到另一个窗口。这样可以确保用户的交互体验更加顺畅。
系统权限控制
作为系统服务之一,WMS 还负责处理权限相关的操作。例如,当应用程序向用户请求弹出悬浮窗权限时,WMS 就会介入其中,确保应用程序只能在得到用户的明确允许后才能使用这个权限。
多窗口模式管理
Android 7.0 及以上版本支持多窗口模式,包括分屏模式和自由窗口模式。WMS 负责分配窗口空间,以及处理多个窗口同时显示的情况。例如,在分屏模式下,WMS 会将屏幕区域分成两个部分,并同时显示两个窗口。
WMS 是 Android 系统中非常核心的一个服务,它完成了很多重要的工作,包括窗口管理、动画效果、输入事件处理、Surface 管理、窗口焦点管理、权限控制和多窗口模式管理等。开发者需要深入了解 WMS 的职责和工作原理,才能更好地设计和优化自己的应用程序。
mPolicy:WindowManagerPolicy
WindowManagerPolicy(WMP)类型的变量。WindowManagerPolicy是窗口管理策略的接口类,用来定义一个窗口策略所要遵循的通用规范,并提供了WindowManager所有的特定的UI行为。它的具体实现类为PhoneWindowManager,这个实现类在WMS创建时被创建。WMP允许定制窗口层级和特殊窗口类型以及关键的调度和布局。
mSessions:ArraySet
ArraySet类型的变量,元素类型为Session。它主要用于进程间通信,其他的应用程序进程想要和WMS进程进行通信就需要经过Session,并且每个应用程序进程都会对应一个Session,WMS保存这些Session用来记录所有向WMS提出窗口管理服务的客户端。
mWindowMap:WindowHashMap
WindowHashMap类型的变量,WindowHashMap继承了HashMap,它限制了HashMap的key值的类型为IBinder,value值的类型为WindowState。WindowState用于保存窗口的信息,在WMS中它用来描述一个窗口。综上得出结论,mWindowMap就是用来保存WMS中各种窗口的集合。
mFinishedStarting:ArrayList
ArrayList类型的变量,元素类型为AppWindowToken,它是WindowToken的子类。要想理解mFinishedStarting的含义,需要先了解WindowToken是什么。
WindowToken
可以理解为窗口令牌,当应用程序想要向WMS申请新创建一个窗口,则需要向WMS出示有效的WindowToken。AppWindowToken作为WindowToken的子类,主要用来描述应用程序的
WindowToken结构,
应用程序中每个Activity都对应一个AppWindowToken。
WindowToken会将相同组件(比如Acitivity)的窗口(WindowState)集合在一起,方便管理。
WindowState
WindowState表示一个窗口的所有属性,且存在于WMS端,所以它是WMS中事实上的窗口。APP端一个Window,就会在WMS端就会有一个WindowState。
mResizingWindows:ArrayList
ArrayList类型的变量,元素类型为WindowState。
mResizingWindows是用来存储正在调整大小的窗口的列表
mInputManager:InputManagerService
InputManagerService类型的变量,输入系统的管理者。InputManagerService(IMS)会对触摸事件进行处理,它会寻找一个最合适的窗口来处理触摸反馈信息,WMS是窗口的管理者,因此,WMS“理所应当”的成为了输入系统的中转站.
WMS的启动流程可以分为以下几个步骤:
在Android系统中,当一个新应用程序需要创建进程时,会由Zygote进程来负责创建。因此,Zygote进程是整个Android系统的“孪生”起源,是高效启动新进程的关键。
在Zygote进程启动之后,它会fork出SystemServer进程,然后等待SystemServer进行初始化。
SystemServer进程负责启动和管理包括WMS在内的所有系统服务。首先,它会调用startBootstrapServices()方法,启动一些最基本的服务,包括Zygote和WMS等。接着,SystemServer会启动ActivityManagerService,并通过ActivityManagerService启动应用程序等其他服务。
WMS服务随着SystemServer的启动而启动,它的具体实现类为PhoneWindowManager。WMS在创建时会执行一系列的初始化操作,包括:
(1)创建InputManagerService对象。输入事件管理器(InputManagerService)是WMS的重要部分,WMS一般通过这个类接收手机的输入事件并发送给一个特定的窗口。
(2)创建PolicyManager对象。PolicyManager是用于管理窗口样式的,主要定义了WMS如何布局窗口、绘制窗口的边框、缩放、旋转等一些与窗口样式相关的内容。
(3)创建SessionManager对象。SessionManager用于与客户端进行通信。当一个应用程序想要创建、移动或删除一个窗口时,它会通过SessionManager将请求发送给WMS服务。
完成初始化后,WMS会等待各种事件的发生。例如,在新的应用程序启动时,WMS会根据应用程序的要求创建新的窗口,并对已有窗口进行调度和更新。在用户打开多个应用程序并在这些应用程序之间切换时,WMS也会负责管理窗口的切换。
WMS addWindow
方法返回的是addWindow的各种状态,比如添加Window成功,无效的Display等,这些状态被定义在WindowManagerGlobal中。在方法里面主要做了四个事情,如果所示:
窗口检查
对参数进行进行检查是非常有必要的第一个步骤,大部分函数中都是这样做的,这个很好理解,毕竟如果传入的参数都是错的,后面做过多的内容都是无用功。
WMS#addWindow对窗口参数主要做了哪些检查。
int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,
appOp);
mPolicy是窗口管理策略的接口,实现类是PhoneWindowManager。在PhoneWindowManager中对窗口的type合法性做了检查。
if (!((type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW)
|| (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW)
|| (type >= FIRST_SYSTEM_WINDOW && type <= LAST_SYSTEM_WINDOW))) {
return WindowManagerGlobal.ADD_INVALID_TYPE;
}
接下来的话通过DisplayId来获取窗口要添加到哪个DisplayContentshang,如果没有找到DisplayContent, 则返回错误状态。
final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
如果窗口是子窗口类型,然后是对父窗口的信息做一些检查,如果为空或者父窗口也是子窗口类型则检查不通过,返回错误类型
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
//通过token获取获取父窗口的信息
parentWindow = windowForClientLocked(null, attrs.token, false);
//如果父窗口为空输入返回信息
if (parentWindow == null) {
ProtoLog.w(WM_ERROR, "Attempted to add window with token that is not a window: "
+ "%s. Aborting.", attrs.token);
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
//如果父窗口也是子窗口类型
if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
&& parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
ProtoLog.w(WM_ERROR, "Attempted to add window with token that is a sub-window: "
+ "%s. Aborting.", attrs.token);
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
}
WindowToken相关处理
在WindowToken相关处理这部分内容中,我们先通过DisplayContent尝试获取WindowToken,token为空且有父窗口,则用父窗口的token,token为空没有父窗口自己新建。
//通过DisplayContent获取到WindowToken
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token);
// If this is a child window, we want to apply the same type checking rules as the
// parent window type.
..........
if (token == null) {
.........
if (hasParent) {
// Use existing parent window token for child windows.
//有父窗口的用父窗口的Token
token = parentWindow.mToken;
} else {
//没有父窗口自己新建一个WindowToken,WindowToken翻译过来是令牌,用于标识一组窗口。
final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
token = new WindowToken(this, binder, type, false, displayContent,
session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
}
}
WindowState的创建和处理
WindowState是WMS端的事实窗口,通过new的方式新建好一个WindowState之后就进行了相关的判断,比如请求添加窗口的客户端是否死亡、窗口的DisplayContent是否失效。
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility, session.mUid, userId,
session.mCanAddInternalSystemWindow);
//窗口是否死亡
if (win.mDeathRecipient == null) {
ProtoLog.w(WM_ERROR, "Adding window client %s"
+ " that is dead, aborting.", client.asBinder());
return WindowManagerGlobal.ADD_APP_EXITING;
}
//DisplayContent是否为空
if (win.getDisplayContent() == null) {
ProtoLog.w(WM_ERROR, "Adding window to Display that has been removed.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
.......
//调用WMP的方法,此方法会根据窗口的Type对LayoutParams的一些成员进行修改
displayPolicy.adjustWindowParamsLw(win, win.mAttrs, callingPid, callingUid);
.......
// 将窗口添加到系统中
res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);
.......
// windowState保存到Map中
mWindowMap.put(client.asBinder(), win);
.......
// 绑定Token和WindowState关系
win.mToken.addWindow(win);
Display的创建和配置
在 Window Manager Service (WMS)
中,当需要创建一个新的 Display
时,WMS 会通过 DisplayManagerService (DMS)
提供的 createDisplay 方法向 SurfaceFlinger
发送请求,请求其创建新的 Display。具体而言,WMS 会调用 DMS 的 createDisplay 方法,该方法会返回一个 DisplayInfo 对象,这个对象包含了新增 Display 的相关信息,如 ID、尺寸、密度等。
然后,WMS 就会根据这些信息往 SurfaceFlinger 发送相应的命令,告诉它需要创建一个新的 Display
,并将其配置为合适的尺寸和密度。SurfaceFlinger 接收到这个命令后,会首先创建一个新的屏幕缓冲区 (Screen Buffer)
,用于保存该 Display 的图像数据。然后,SurfaceFlinger 会按照指定的尺寸和密度来初始化该 Display,并分配对应的帧缓冲区 (Frame Buffer)
。
在初始化完成后,SurfaceFlinger 就会将该 Display 的 Surface (也就是屏幕缓冲区) 绑定到指定的硬件显示设备上,并将其设置为可见状态
。此时,该 Display 就已经被成功创建并且显示了出来。
需要注意的是,SurfaceFlinger 可以同时支持多个 Display
,每个 Display 对应一个 Screen Buffer 和一个 Frame Buffer,它们之间实现了双缓冲机制
。这种机制可以保证在进行图像渲染和显示的过程中,不会出现屏幕闪烁或者撕裂等问题。
在创建和配置 Display 的过程中,WMS 主要负责向 DMS 和 SurfaceFlinger 发送相应的命令,而实际的创建和配置工作则是由 SurfaceFlinger 来完成的。这种分工既能确保系统的稳定性和安全性,又能实现高效的屏幕渲染和显示。
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
在WindowManagerGlobal方法中,会删除相关的一些数据,如ViewRootImpl、LayoutParams、DecorView,并将DecorView加入到死亡列表中。
void doRemoveView(ViewRootImpl root) {
synchronized (mLock) {
//从ViewRootImpl获取到索引值
final int index = mRoots.indexOf(root);
if (index >= 0) {
//删除ViewRootImpl列表中的数据
mRoots.remove(index);
//删除LayoutParams列表中的数据
mParams.remove(index);
//删除DecorView列表中的数据
final View view = mViews.remove(index);
//DecorView加入到死亡列表
mDyingViews.remove(view);
}
......
}
......
}
这ViewRootImpl中die方法中,会先判断是否立即执行删除,如果立即执行则调用doDie方法,如果不是则通过Handler方法执行删除的信号,等待删除。
boolean die(boolean immediate) {
// Make sure we do execute immediately if we are in the middle of a traversal or the damage
// done by dispatchDetachedFromWindow will cause havoc on return.
//immediate 是否立即执行 为ture则立即执行
if (immediate && !mIsInTraversal) {
doDie();
return false;
}
......
//通过Handler发送删除信息,等待删除
mHandler.sendEmptyMessage(MSG_DIE);
return true;
}
void doDie() {
//检查线程
checkThread();
if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface);
synchronized (this) {
//判断是否删除
if (mRemoved) {
return;
}
//防止重复调用
mRemoved = true;
if (mAdded) {
//做数据清除 注销操作,调用session的remove方法
dispatchDetachedFromWindow();
}
if (mAdded && !mFirst) {
destroyHardwareRenderer();
if (mView != null) {
int viewVisibility = mView.getVisibility();
boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
if (mWindowAttributesChanged || viewVisibilityChanged) {
// If layout params have been changed, first give them
// to the window manager to make sure it has the correct
// animation info.
try {
if ((relayoutWindow(mWindowAttributes, viewVisibility, false)
& WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
mWindowSession.finishDrawing(
mWindow, null /* postDrawTransaction */);
}
} catch (RemoteException e) {
}
}
//销毁画布
destroySurface();
}
}
mAdded = false;
}
//调用WindowManagerGlobal移除方法
WindowManagerGlobal.getInstance().doRemoveView(this);
}
在ViewRootImpl的dispatchDetachedFromWindow方法中会调用Session与WMS进行通信,然后执行移除的操作。
在WMS的removeWindow函数中,先会通过Session和Client获取到当前窗口在WMS的副本也就是WindowState,如果不为空则执行删除操作。
void removeWindow(Session session, IWindow client) {
synchronized (mGlobalLock) {
//获取WindowState
WindowState win = windowForClientLocked(session, client, false);
if (win != null) {
//执行删除
win.removeIfPossible();
return;
}
// Remove embedded window map if the token belongs to an embedded window
mEmbeddedWindowController.remove(client);
}
}
win.removeIfPossible方法和它的名字一样,并不是直接执行删除操作,而是进行多个条件判断过滤,满足其中一个条件就会return,推迟删除操作。比如V正在运行一个动画,这是就会推迟删除操作知道动画完成。然后调用removeImmediately方法。
void removeImmediately() {
super.removeImmediately();
//已经删除
if (mRemoved) {
// Nothing to do.
ProtoLog.v(WM_DEBUG_ADD_REMOVE,
"WS.removeImmediately: %s Already removed...", this);
return;
}
//移除标记
mRemoved = true;
......
final DisplayContent dc = getDisplayContent();
......
//policy做移除操作
dc.getDisplayPolicy().removeWindowLw(this);
//关闭输入事件渠道
disposeInputChannel();
mWinAnimator.destroyDeferredSurfaceLocked();
mWinAnimator.destroySurfaceLocked();
//Session集合冲移除WindowState
mSession.windowRemovedLocked();
.....
//集中处理清除工作
mWmService.postWindowRemoveCleanupLocked(this);
}