Android开发技术——车机技术之WMS学习

窗口管理核心类介绍

窗口管理使用到的 DisplayContent,WindowToken 和 WindowState。

DisplayContent

用来管理一个逻辑屏上的所有窗口,有几个屏幕就会有几个 DisplayContent。使用 displayId 来区分。

处于不同 DisplayContent 的两个窗口在布局、显示顺序以及动画处理上不会产生任何耦合。因此,就这几个方面来说,DisplayContent 就像一个孤岛,所有这些操作都可以在其内部独立执行。

DisplayContent 类声明:

class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowContainer>{
    
        // Mapping from a token IBinder to a WindowToken object on this display.    private final HashMap<IBinder, WindowToken> mTokenMap = new HashMap();}   

DisplayContent 的子容器是其内部类 DisplayChildWindowContainer
DisplayContent 内部使用:IBinder 为 key,WindowToken 为 value 的键值对保存在 HashMap 中。
DisplayContent 是在 Window 添加到 WMS 的时候初始化的。

WMS

public int addWindow(Session session, ..){
    
        final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);    ..}

mRoot 是 RootWindowContainer 类型的对象,看名字就知道其是窗口容器的根。说明在 Window 体系中,RootWindowContainer 节点是容器最顶端的父容器。

class RootWindowContainer extends WindowContainer<DisplayContent> {
    
        DisplayContent getDisplayContentOrCreate(int displayId) {
    
            DisplayContent dc = getDisplayContent(displayId);        if (dc == null) {
    
                final Display display = mService.mDisplayManager.getDisplay(displayId);            if (display != null) {
    
                                  dc = createDisplayContent(display);                     }        }        return dc;    }   DisplayContent getDisplayContent(int displayId) {
    
          for (int i = mChildren.size() - 1; i >= 0; --i) {
    
              final DisplayContent current = mChildren.get(i);           if (current.getDisplayId() == displayId) {
    
                  return current;           }      }      return null;  }}

其继承了 WindowContainer,这里的 DisplayContent 是一个泛型声明,表示其子容器的类型是 DisplayContent, 在 getDisplayContent 方法中也可知,其 mChildren 列表是 DisplayContent 的集合。这也变相的说明 DisplayContent 也是一个容器。

WindowToken
类声明:

class WindowToken extends WindowContainer<WindowState>

表明 WindowToken 也是子容器,其子容器是 WindowState,所以 WindowState 也是一个容器。

WindowToken 在窗口体系中有两个作用:

    应用组件标识:将属于同一个应用组件的窗口组织在一起,这里的应用组件可以是:ActivityInputMethodWallpaper 以及 Dream。WMS 在对窗口的管理过程中用 WindowToken 来指代一个应用组件。例如在进行窗口的 Z-Order 排序时,属于同一个 WindowToken 的窗口会被安排在一起。
    令牌作用:WindowToken 由应用组件或其管理者负责向 WMS 声明并持有,应用组件在需要更新窗口时,需要向 WMS 提供令牌表明自己的身份, 并且窗口的类型必须与所持有的 WindowToken 的类型一致。

但是系统窗口是个例外,并不需要提供 token,WMS 会隐式声明一个WindowToken。那是不是说谁都可以添加系统窗口了呢?非也,在 addWindow 开始处就会调用下面代码:

mPolicy.checkAddPermission()

它要求客户端必须拥有 SYSTEM_ALERT_WINDOW 或INTERNAL_SYSTEM_WINDOW 权限才能创建系统类型的窗口。Window 和WindowToken 关系如下:

在这里插入图片描述WindowState
类声明:

class WindowState extends WindowContainer

表明 WindowState 也是一个 WindowContainer 容器,但是其子容器也是WindowState,一般窗口有子窗口 SUB_WINDOW 的情况下,WindowState 才有子容器节点。WindowState 在 WMS 中表示一个 Window 窗口状态属性,其内部保存了一个 Window 所有的属性信息。其与 View 以及 WindowToken 关系如下:
在这里插入图片描述如何查看当前设备 Window 窗口状态命令?

adb shell dumpsys window windows    
Window #9 Window{
    
    884cb45 u0 com.android.messaging/com.android.messaging.ui.conversationlist.ConversationListActivity}:    mDisplayId=0 stackId=3 mSession=Session{
    
    f1b7b8e 4307:u0a10065} mClient=android.os.BinderProxy@a512fbc    mOwnerUid=10065 mShowToOwnerOnly=true package=com.android.messaging appop=NONE    mAttrs={
    
    (0,36)(828xwrap) gr=BOTTOM CENTER sim={
    
    adjust=pan forwardNavigation} ty=APPLICATION fmt=TRANSLUCENT wanim=0x7f130015      fl=DIM_BEHIND ALT_FOCUSABLE_IM HARDWARE_ACCELERATED      vsysui=LIGHT_STATUS_BAR LIGHT_NAVIGATION_BAR}    Requested w=828 h=290 mLayoutSeq=220    mBaseLayer=21000 mSubLayer=0    mToken=AppWindowToken{
    
    3f9efb8 token=Token{
    
    2b272cc ActivityRecord{
    
    55a41e u0 com.android.messaging/.ui.conversationlist.ConversationListActivity t8}}}    mAppToken=AppWindowToken{
    
    3f9efb8 token=Token{
    
    2b272cc ActivityRecord{
    
    55a41e u0 com.android.messaging/.ui.conversationlist.ConversationListActivity t8}}}...  

下面笔者以窗口的添加操作为例讲解 WMS 的窗口管理。

窗口的添加操作

public int addWindow(Session session, IWindow client, int seq,        WindowManager.LayoutParams attrs, int viewVisibility, int displayId,        Rect outContentInsets, Rect outStableInsets, Rect outOutsets,        InputChannel outInputChannel) {
    
        ...    int res = mPolicy.checkAddPermission(attrs, appOp);//1    ...    synchronized(mWindowMap) {        final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);//2        if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {            parentWindow = windowForClientLocked(null, attrs.token, false);//3        }        ...                    WindowToken token = displayContent.getWindowToken(                hasParent ? parentWindow.mAttrs.token : attrs.token);//4        if (token == null) {                                          final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();            token = new WindowToken(this, binder, type, false, displayContent,                    session.mCanAddInternalSystemWindow);//5        }         ...        final WindowState win = new WindowState(this, session, client, token, parentWindow,                appOp[0], seq, attrs, viewVisibility, session.mUid,                session.mCanAddInternalSystemWindow);//6        ...        mPolicy.adjustWindowParamsLw(win.mAttrs);//7        ...        if  (openInputChannels) {            win.openInputChannel(outInputChannel);//8        }        ...        mWindowMap.put(client.asBinder(), win);//9        ...        win.mToken.addWindow(win);//10        ...        displayContent.assignWindowLayers(false /* setLayoutNeeded */);//11        //12        if (focusChanged) {            mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);        }        mInputMonitor.updateInputWindowsLw(false /*force*/);    }    ...    return res;}

注释1:检查当前 Window 的 token 等权限合法性。
注释2:这在介绍 DisplayContent 已经说过,使用 RootWindowContainer 的子容器中获取一个 DisplayContent,如果子容器集合中不存在,则去获取一个,并添加到 child 集合中
注释3:如果是 Dialog 等子窗口,则获取父窗口,没有就报找不到父窗口的异常。
注释4:使用 attr.token 去 displayContent 的键值对 mTokenMap 中获取对应的 WindowToken,WindowToken 中保存了一组 Window。
注释5:如果4中 WindowToken 为 null,则创建一个 WindowToken,传入 app 层传入的 attr.token 以及 displayContent 对象,内部会将这个创建的 WindowToken 保存到 displayContent 中
注释6:创建一个 WindowState,并传入所有关于 Window 相关的属性,这样 WindowState 在 WMS 中就是以一个 Window 性质存在了、WindowState 构造过程中会将其添加到 WindowToken 中去。
注释7:根据 mPolicy 调整 window 的 attr 属性,mPolicy 的实现类是PhoneManagerPolicy。
注释8:执行 WindowState 的 openInputChannel,这里主要是打通和 Input 系统的通道,用于接收 IMS 的输入事件请求。
注释9:将客户端 app 层的 Window 对象和 WindowState 关联上,这样 WMS 就可以通过 Window 找到 WMS 中的 WindowState 对象。
注释10:win.mToken 是前面创建的 WindowToken 对象,所以此处就是将WindowState 加入到 WindowToken 的子容器集合中。
注释11:分配窗口的层级,这里的 setLayoutNeeded 参数为 false,说明不需要进行 Layout 操作。

这里小结下 addWindow 方法,主要就是创建了一个和 Window 一一对应的 WindowState 对象,并将 WindowState 插入到父容器 WindowToken 的子容器集合中,而 WindowToken 又保存在 DisplayContent 的键值对集合中。三种关系可以简单总结如下:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_24252589/article/details/131353002