【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<Session>

ArraySet类型的变量,元素类型为Session。它主要用于进程间通信,其他的应用程序进程想要和WMS进程进行通信就需要经过Session,并且每个应用程序进程都会对应一个Session,WMS保存这些Session用来记录所有向WMS提出窗口管理服务的客户端。

mWindowMap:WindowHashMap

WindowHashMap类型的变量,WindowHashMap继承了HashMap,它限制了HashMap的key值的类型为IBinder,value值的类型为WindowState。WindowState用于保存窗口的信息,在WMS中它用来描述一个窗口。综上得出结论,mWindowMap就是用来保存WMS中各种窗口的集合。

mFinishedStarting:ArrayList<AppWindowToken>

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<WindowState>

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);
    }
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值