• 【Android】WMS(四)WMS职责


    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);
    
    • 1
    • 2

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    接下来的话通过DisplayId来获取窗口要添加到哪个DisplayContentshang,如果没有找到DisplayContent, 则返回错误状态。

    final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
    
    • 1

    如果窗口是子窗口类型,然后是对父窗口的信息做一些检查,如果为空或者父窗口也是子窗口类型则检查不通过,返回错误类型

    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;
                    }
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    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);
                    }
                }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    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;
                }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    .......
    //调用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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    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.");
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    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);
                }
                ......
            }
        ......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    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);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    在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);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    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);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
  • 相关阅读:
    魔性洗脑神曲掀起模仿热潮,品牌为何热衷“打歌”?
    【NLP】第14章 解释黑盒Transformer模型
    瑞吉外卖03-新增员工
    Java实现图书管理系统
    Linux命令行
    C# Socket通信从入门到精通(9)——如何设置本机Ip地址
    java:数组缩减
    Linux常用指令
    写给Python社群的第7课:用 Python 模块了不起呀?就是了不起
    照片编辑和管理软件:Lightroom Classic 2022 Mac
  • 原文地址:https://blog.csdn.net/qq_43358469/article/details/131125458