多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
经过上一节的介绍,读者应该对WMS的窗口管理有了一个感性的认识。从这一节开将深入WMS的内部去剖析其工作流程。 根据前述内容可知,SampleWindow添加窗口的函数是IWindowSession.add()。IWindowSession是WMS与客户端交互的一个代理,add则直接调用到了WMS的addWindow()函数。我们将从这个函数开始WMS之旅。本小节只讨论它的前半部分。 >[info] **注意**: 由于篇幅所限,本章不准备讨论removeWindow的实现。 **WindowManagerService.java::WindowManagerService.addWindow()Part1** ``` public int addWindow(Session session, IWindowclient, int seq, WindowManager.LayoutParams attrs, int viewVisibility,int displayId Rect outContentInsets, InputChannel outInputChannel) { // 首先检查权限,没有权限的客户端不能添加窗口 intres = mPolicy.checkAddPermission(attrs); ...... // 当为某个窗口添加子窗口时,attachedWindow将用来保存父窗口的实例 WindowState attachedWindow = null; //win就是即将被添加的窗口了 WindowState win = null; ...... finalint type = attrs.type; synchronized(mWindowMap){ ...... //①获取窗口要添加到的DisplayContent /* 在添加窗口时,必须通过displayId参数指定添加到哪一个DisplayContent。 SampleWindow例子没有指定displayId参数,Session会替SampleWindow选择 DEFAULT_DISPLAY,也就是手机屏幕 */ finalDisplayContent displayContent = getDisplayContentLocked(displayId); if(displayContent == null) { return WindowManagerGlobal.ADD_INVALID_DISPLAY; } ...... // 如果要添加的窗口是另一个的子窗口,就要求父窗口必须已经存在 // 注意, attrs.type表示了窗口的类型,attrs.token则表示了窗口所隶属的对象 // 对于子窗口来说,attrs.token表示了父窗口 if(type >= FIRST_SUB_WINDOW &&.type <= LAST_SUB_WINDOW) { attachedWindow = windowForClientLocked(null, attrs.token, false); if (attachedWindow == null) { return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN; } //在这里还可以看出WMS要求窗口的层级关系最多为两层 if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW &&attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) { return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN; } } booleanaddToken = false; // **②WindowToken出场!**根据客户端的attrs.token取出已注册的WindowToken WindowToken token = mTokenMap.get(attrs.token); // 下面的if语句块初步揭示了WindowToken和窗口之间的关系 if(token == null) { // 对于以下几种类型的窗口,必须通过LayoutParams.token成员为其指定一个已经 // 添加至WMS的WindowToken if (type >= FIRST_APPLICATION_WINDOW && type<= LAST_APPLICATION_WINDOW) { return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } if (type == TYPE_INPUT_METHOD) { return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } if (type == TYPE_WALLPAPER) { return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } if (type == TYPE_DREAM) { return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } // 其他类型的窗口则不需要事先向WMS添加WindowToken因为WMS会在这里隐式地创 // 建一个。注意最后一个参数false,这表示此WindowToken由WMS隐式创建。 token = new WindowToken(this, attrs.token, -1, false); addToken = true; } else if (type >= FIRST_APPLICATION_WINDOW &&type <= LAST_APPLICATION_WINDOW) { // 对于APPLICATION类型的窗口,要求对应的WindowToken的类型也为APPLICATION // 并且是WindowToken的子类:AppWindowToken AppWindowToken atoken = token.appWindowToken; if (atoken == null) { return WindowManagerImpl.ADD_NOT_APP_TOKEN; } else if (atoken.removed) { returnWindowManagerImpl.ADD_APP_EXITING; } if (type==TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn){ return WindowManagerImpl.ADD_STARTING_NOT_NEEDED; } } else if (type == TYPE_INPUT_METHOD) { // 对于其他几种类型的窗口也有类似的要求:窗口类型必须与WindowToken的类型一致 if (token.windowType != TYPE_INPUT_METHOD) { return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } } else if (type == TYPE_WALLPAPER) { if (token.windowType != TYPE_WALLPAPER) { return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } } else if (type == TYPE_DREAM) { if (token.windowType != TYPE_DREAM) { return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } } // ③WMS为要添加的窗口创建了一个WindowState对象 // 这个对象维护了一个窗口的所有状态信息 win= new WindowState(this, session, client, token, attachedWindow,seq, attrs, viewVisibility, displayContent); ...... // WindowManagerPolicy出场了。这个函数的调用会调整LayoutParams的一些成员的取值 mPolicy.adjustWindowParamsLw(win.mAttrs); res= mPolicy.prepareAddWindowLw(win, attrs); if(res != WindowManagerGlobal.ADD_OKAY) { return res; } // 接下来将刚刚隐式创建的WindowToken添加到mTokenMap中去。通过这行代码应该 //读者应该能想到,所有的WindowToken都被放入这个HashTable中 ...... if(addToken) { mTokenMap.put(attrs.token, token); } win.attach(); // 然后将WindowState对象加入到mWindowMap中 mWindowMap.put(client.asBinder(),win); // 剩下的代码稍后再做分析 ...... } } ``` addWindow()函数的前段代码展示了三个重要的概念,分别是WindowToken、WindowState以及DisplayContent。并且在函数开始处对窗口类型的检查判断也初步揭示了它们之间的关系:除子窗口外,添加任何一个窗口都必须指明其所属的WindowToken;窗口在WMS中通过一个WindowState实例进行管理和保管。同时必须在窗口中指明其所属的DisplayContent,以便确定窗口将被显示到哪一个屏幕上。