WMS职责

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 的职责和工作原理,才能更好地设计和优化自己的应用程序。

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服务的启动

WMS的启动流程可以分为以下几个步骤:

Zygote进程启动

在Android系统中,当一个新应用程序需要创建进程时,会由Zygote进程来负责创建。因此,Zygote进程是整个Android系统的“孪生”起源,是高效启动新进程的关键。

SystemServer进程启动

在Zygote进程启动之后,它会fork出SystemServer进程,然后等待SystemServer进行初始化。

SystemServer初始化

SystemServer进程负责启动和管理包括WMS在内的所有系统服务。首先,它会调用startBootstrapServices()方法,启动一些最基本的服务,包括Zygote和WMS等。接着,SystemServer会启动ActivityManagerService,并通过ActivityManagerService启动应用程序等其他服务。

WMS创建

WMS服务随着SystemServer的启动而启动,它的具体实现类为PhoneWindowManager。WMS在创建时会执行一系列的初始化操作,包括:

(1)创建InputManagerService对象。输入事件管理器(InputManagerService)是WMS的重要部分,WMS一般通过这个类接收手机的输入事件并发送给一个特定的窗口。

(2)创建PolicyManager对象。PolicyManager是用于管理窗口样式的,主要定义了WMS如何布局窗口、绘制窗口的边框、缩放、旋转等一些与窗口样式相关的内容。

(3)创建SessionManager对象。SessionManager用于与客户端进行通信。当一个应用程序想要创建、移动或删除一个窗口时,它会通过SessionManager将请求发送给WMS服务。

WMS启动

完成初始化后,WMS会等待各种事件的发生。例如,在新的应用程序启动时,WMS会根据应用程序的要求创建新的窗口,并对已有窗口进行调度和更新。在用户打开多个应用程序并在这些应用程序之间切换时,WMS也会负责管理窗口的切换。

Window添加(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 来完成的。这种分工既能确保系统的稳定性和安全性,又能实现高效的屏幕渲染和显示。

Window删除

1 检查线程的正确性

void checkThread() {

if (mThread != Thread.currentThread()) {

throw new CalledFromWrongThreadException(

"Only the original thread that created a view hierarchy can touch its views.");

}

}

2 ViewRootImpl相关数据删除

在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);

}

......

}

......

}

3 判断是否立即执行删除

这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);

}

好文阅读

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。