【22】Android高级知识之Window(三) -WMS

一、概述

这次开始到了WindowManagerService(WMS),你可以把它看做一个WindowManager,只不过呢,属于系统服务进程(system_server)中的一员,和应用不在同一进程,所以涉及了一些跨进程通信的内容,如果不清楚的可以去补一下Binder通信机制。这些不是重点,这次重点讲的是上堂课没有讲完的addToDisplayAsUser。

二、WMS

关于WMS启动的过程,之后会和AMS(ATMS)文章中一起讲解。如果已经有了解Android特有的跨进程通信方式Binder的基础,不难知道WMS是一个Binder对象,这对之后的讲解有点帮助。

上篇Window的文章不知道大家还有没有印象mWindowSession#addToDisplayAsUser,这里的Session是通过WindowManagerGlobal#getWindowSession提供的binder对象,帮助我们完成跨进程通信。

	//WindowManagerGlobal.java
    @UnsupportedAppUsage
    public static IWindowManager getWindowManagerService() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
                sWindowManagerService = IWindowManager.Stub.asInterface(
                        ServiceManager.getService("window"));
                try {
                    if (sWindowManagerService != null) {
                        ValueAnimator.setDurationScale(
                                sWindowManagerService.getCurrentAnimatorScale());
                        sUseBLASTAdapter = sWindowManagerService.useBLAST();
                    }
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowManagerService;
        }
    }

    @UnsupportedAppUsage
    public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    // Emulate the legacy behavior.  The global instance of InputMethodManager
                    // was instantiated here.
                    // TODO(b/116157766): Remove this hack after cleaning up @UnsupportedAppUsage
                    InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();
                    IWindowManager windowManager = getWindowManagerService();
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            });
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowSession;
        }
    }

这段代码很长,但是很简单,目的是请求服务端的WindowSession对象,即服务端的binder对象。我们看它是怎么做的。首先先通过IPC通信方式获取WMS,之前说过这也是一个binder对象。然后借助WMS的openSession拿到服务端的WindowSession对象。这样做的好处是什么呢?将两次binder通信简化成了一次binder通信,优化了跨进程通信的效率。

服务端的WindowSession对象是一个叫做Session的类。

既然已经进入Session这个类,我们直接来看一下addToDisplayAsUser做了什么。

    //Session.java
    @Override
    public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, @InsetsType int requestedVisibleTypes,
            InputChannel outInputChannel, InsetsState outInsetsState,
            InsetsSourceControl.Array outActiveControls, Rect outAttachedFrame,
            float[] outSizeCompatScale) {
        return mService.addWindow(this, window, attrs, viewVisibility, displayId,
                UserHandle.getUserId(mUid), requestedVisibleTypes, outInputChannel, outInsetsState,
                outActiveControls, outAttachedFrame, outSizeCompatScale);
    }

    @Override
    public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, int userId, @InsetsType int requestedVisibleTypes,
            InputChannel outInputChannel, InsetsState outInsetsState,
            InsetsSourceControl.Array outActiveControls, Rect outAttachedFrame,
            float[] outSizeCompatScale) {
        return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,
                requestedVisibleTypes, outInputChannel, outInsetsState, outActiveControls,
                outAttachedFrame, outSizeCompatScale);
    }

它调用了内部的addToDisplay方法,一般我会把这种行为相同,但是命名有些许不同的方法叫做衍生方法。这里面的mService就是WMS,因为它们属于同一个进程,所以这里的调用已经不是跨进程通信了。并且调用了WMS#addWindow方法。

上面跟了一大段,我们发现现在才是真正进入了WMS,可见系统源码对于职责的封装不可谓是不严格啊,我们继续分析。

//WindowManagerService.java
    public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
            int displayId, int requestUserId, @InsetsType int requestedVisibleTypes,
            InputChannel outInputChannel, InsetsState outInsetsState,
            InsetsSourceControl.Array outActiveControls, Rect outAttachedFrame,
            float[] outSizeCompatScale) {

	...
	//获取屏幕内容对象,根据屏幕id(一屏多显可能也会有多个id)
	final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
	
	...
	//一个window集合,保存的key是ViewRootImpl$w的binder对象(也可以理解为是window的binder对象)
	if (mWindowMap.containsKey(client.asBinder())) {
                ProtoLog.w(WM_ERROR, "Window %s is already added", client);
                return WindowManagerGlobal.ADD_DUPLICATE_ADD;
            }

	...
	//创建WindowState,服务端管理的window,记录了window全部信息
final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], attrs, viewVisibility, session.mUid, userId,
                    session.mCanAddInternalSystemWindow);

	...
	win.attach();
	//添加到map集合中,key是客户端w的binder对象,value是WindwoState
    mWindowMap.put(client.asBinder(), win);
    win.initAppOpsState();
	
	...
	//WindowState拿到token添加当前的WindowState进去,双向绑定
	win.mToken.addWindow(win);

由于WMS#addWindow篇幅很多,省略了很多细节之后看上去就清楚多了。重要的事情注解都有说明:

  • 通过DisplayContent获取token
  • 根据客户端window创建WindowState对象,保存Window有关的所有信息
  • 把WindowState缓存到WindowMap集合中,key是客户端的w的binder对象
  • 将WindowState和token双向绑定

三、总结

1、ViewRootImpl#setView通过WindwoSession来管理window
2、借助WMS的binder对象调用openSession来获取WindowSession对象,服务端叫Session
3、WMS#addWindow根据客户端window创建了对应的WindowState对象
4、用一个WindowMap集合缓存,key是客户端ViewRootImpl$W的binder对象
5、WindowState和WindowToken双向绑定

至此关于ViewRootImpl#setView中的关于WMS#addToDisplayAsUser的逻辑我们就分析完了。这样一趟分析下来,发现其实也没有做多少事,但是有一点好奇的不知道大家有没有注意到,方法是setView,而WMS中全程操作的对象都是window,并且setView方法中的View并没有通过addToDisplayAsUser方法传递下去。那么View之后又是怎么处理的呢?我们下一篇文章中继续探索Window的真相。

相关推荐

  1. 22Android高级知识Window() -WMS

    2024-07-20 08:50:06       17 阅读
  2. 【15】Android基础知识Window(二) - ViewRootImpl

    2024-07-20 08:50:06       19 阅读
  3. Android 13 - Media框架(22)- MediaCodec(

    2024-07-20 08:50:06       53 阅读
  4. Android 知识总结第二篇

    2024-07-20 08:50:06       56 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-20 08:50:06       52 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-20 08:50:06       54 阅读
  3. 在Django里面运行非项目文件

    2024-07-20 08:50:06       45 阅读
  4. Python语言-面向对象

    2024-07-20 08:50:06       55 阅读

热门阅读

  1. HMACSHA256的原理和在C# 中的使用

    2024-07-20 08:50:06       19 阅读
  2. 内网渗透简介

    2024-07-20 08:50:06       18 阅读
  3. Go网络编程-HTTP程序设计_2

    2024-07-20 08:50:06       19 阅读
  4. 基于Go 1.19的站点模板爬虫

    2024-07-20 08:50:06       16 阅读
  5. 财迷换钱

    2024-07-20 08:50:06       16 阅读
  6. 计数,桶与基数排序

    2024-07-20 08:50:06       20 阅读