前言

在本系列的上一篇文章中,我们学习了WMS的诞生,WMS被创建后,它的重要的成员有哪些?Window添加过程的WMS部分做了什么呢?这篇文章会给你解答。

1.WMS的重要成员

所谓WMS的重要成员是指WMS中的重要的成员变量,如下所示。
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

  1. final WindowManagerPolicy mPolicy;
  2. final IActivityManager mActivityManager;
  3. final ActivityManagerInternal mAmInternal;
  4. final AppOpsManager mAppOps;
  5. final DisplaySettings mDisplaySettings;
  6. ...
  7. final ArraySet<Session> mSessions = new ArraySet<>();
  8. final WindowHashMap mWindowMap = new WindowHashMap();
  9. final ArrayList<AppWindowToken> mFinishedStarting = new ArrayList<>();
  10. final ArrayList<AppWindowToken> mFinishedEarlyAnim = new ArrayList<>();
  11. final ArrayList<AppWindowToken> mWindowReplacementTimeouts = new ArrayList<>();
  12. final ArrayList<WindowState> mResizingWindows = new ArrayList<>();
  13. final ArrayList<WindowState> mPendingRemove = new ArrayList<>();
  14. WindowState[] mPendingRemoveTmp = new WindowState[];
  15. final ArrayList<WindowState> mDestroySurface = new ArrayList<>();
  16. final ArrayList<WindowState> mDestroyPreservedSurface = new ArrayList<>();
  17. ...
  18. final H mH = new H();
  19. ...
  20. final WindowAnimator mAnimator;
  21. ...
  22. final InputManagerService mInputManager

这里列出了WMS的部分成员变量,下面分别对它们进行简单的介绍。

mPolicy:WindowManagerPolicy
WindowManagerPolicy(WMP)类型的变量。WindowManagerPolicy是窗口管理策略的接口类,用来定义一个窗口策略所要遵循的通用规范,并提供了WindowManager所有的特定的UI行为。它的具体实现类为PhoneWindowManager,这个实现类在WMS创建时被创建。WMP允许定制窗口层级和特殊窗口类型以及关键的调度和布局。

mSessions:ArraySet
ArraySet类型的变量,元素类型为Session。在Android解析WindowManager(三)Window的添加过程这篇文章中我提到过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)集合在一起,方便管理。

mFinishedStarting就是用于存储已经完成启动的应用程序窗口(比如Acitivity)的AppWindowToken的列表。
除了mFinishedStarting,还有类似的mFinishedEarlyAnim和mWindowReplacementTimeouts,其中mFinishedEarlyAnim存储了已经完成窗口绘制并且不需要展示任何已保存surface的应用程序窗口的AppWindowToken。mWindowReplacementTimeout存储了等待更换的应用程序窗口的AppWindowToken,如果更换不及时,旧窗口就需要被处理。

mResizingWindows:ArrayList
ArrayList类型的变量,元素类型为WindowState。
mResizingWindows是用来存储正在调整大小的窗口的列表。与mResizingWindows类似的还有mPendingRemove、mDestroySurface和mDestroyPreservedSurface等等。其中mPendingRemove是在内存耗尽时设置的,里面存有需要强制删除的窗口。mDestroySurface里面存有需要被Destroy的Surface。mDestroyPreservedSurface里面存有窗口需要保存的等待销毁的Surface,为什么窗口要保存这些Surface?这是因为当窗口经历Surface变化时,窗口需要一直保持旧Surface,直到新Surface的第一帧绘制完成。

mAnimator:WindowAnimator
WindowAnimator类型的变量,用于管理窗口的动画以及特效动画。

mH:H
H类型的变量,系统的Handler类,用于将任务加入到主线程的消息队列中,这样代码逻辑就会在主线程中执行。

mInputManager:InputManagerService
InputManagerService类型的变量,输入系统的管理者。InputManagerService(IMS)会对触摸事件进行处理,它会寻找一个最合适的窗口来处理触摸反馈信息,WMS是窗口的管理者,因此,WMS“理所应当”的成为了输入系统的中转站,WMS包含了IMS的引用不足为怪。

2.Window的添加过程(WMS部分)

我们知道Window的操作分为两大部分,一部分是WindowManager处理部分,另一部分是WMS处理部分,如下所示。
Android解析WindowManager(三)Window的添加过程这篇文章中,我讲解了Window的添加过程的WindowManager处理部分,这一篇文章我们接着来学习Window的添加过程的WMS部分。
无论是系统窗口还是Activity,它们的Window的添加过程都会调用WMS的addWindow方法,由于这个方法代码逻辑比较多,这里分为3个部分来阅读。
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

addWindow方法part1

  1. public int addWindow(Session session, IWindow client, int seq,
  2. WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
  3. Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
  4. InputChannel outInputChannel) {
  5.  
  6. int[] appOp = new int[];
  7. int res = mPolicy.checkAddPermission(attrs, appOp);//
  8. if (res != WindowManagerGlobal.ADD_OKAY) {
  9. return res;
  10. }
  11. ...
  12. synchronized(mWindowMap) {
  13. if (!mDisplayReady) {
  14. throw new IllegalStateException("Display has not been initialialized");
  15. }
  16. final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);//
  17. if (displayContent == null) {
  18. Slog.w(TAG_WM, "Attempted to add window to a display that does not exist: "
  19. + displayId + ". Aborting.");
  20. return WindowManagerGlobal.ADD_INVALID_DISPLAY;
  21. }
  22. ...
  23. if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {//
  24. parentWindow = windowForClientLocked(null, attrs.token, false);//
  25. if (parentWindow == null) {
  26. Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "
  27. + attrs.token + ". Aborting.");
  28. return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
  29. }
  30. if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
  31. && parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
  32. Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "
  33. + attrs.token + ". Aborting.");
  34. return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
  35. }
  36. }
  37. ...
  38. }
  39. ...
  40. }

WMS的addWindow返回的是addWindow的各种状态,比如添加Window成功,无效的display等等,这些状态被定义在WindowManagerGlobal中。
注释1处根据Window的属性,调用WMP的checkAddPermission方法来检查权限,具体的实现在PhoneWindowManager的checkAddPermission方法中,如果没有权限则不会执行后续的代码逻辑。注释2处通过displayId来获得窗口要添加到哪个DisplayContent上,如果没有找到DisplayContent,则返回WindowManagerGlobal.ADD_INVALID_DISPLAY这一状态,其中DisplayContent用来描述一块屏幕。注释3处,type代表一个窗口的类型,它的数值介于FIRST_SUB_WINDOW和LAST_SUB_WINDOW之间(1000~1999),这个数值定义在WindowManager中,说明这个窗口是一个子窗口,不了解窗口类型取值范围的请阅读Android解析WindowManager(二)Window的属性这篇文章。注释4处,attrs.token是IBinder类型的对象,windowForClientLocked方法内部会根据attrs.token作为key值从mWindowMap中得到该子窗口的父窗口。接着对父窗口进行判断,如果父窗口为null或者type的取值范围不正确则会返回错误的状态。

addWindow方法part2

  1. ...
  2. AppWindowToken atoken = null;
  3. final boolean hasParent = parentWindow != null;
  4. WindowToken token = displayContent.getWindowToken(
  5. hasParent ? parentWindow.mAttrs.token : attrs.token);//
  6. final int rootType = hasParent ? parentWindow.mAttrs.type : type;//
  7. boolean addToastWindowRequiresToken = false;
  8.  
  9. if (token == null) {
  10. if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
  11. Slog.w(TAG_WM, "Attempted to add application window with unknown token "
  12. + attrs.token + ". Aborting.");
  13. return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
  14. }
  15. if (rootType == TYPE_INPUT_METHOD) {
  16. Slog.w(TAG_WM, "Attempted to add input method window with unknown token "
  17. + attrs.token + ". Aborting.");
  18. return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
  19. }
  20. if (rootType == TYPE_VOICE_INTERACTION) {
  21. Slog.w(TAG_WM, "Attempted to add voice interaction window with unknown token "
  22. + attrs.token + ". Aborting.");
  23. return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
  24. }
  25. if (rootType == TYPE_WALLPAPER) {
  26. Slog.w(TAG_WM, "Attempted to add wallpaper window with unknown token "
  27. + attrs.token + ". Aborting.");
  28. return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
  29. }
  30. ...
  31. if (type == TYPE_TOAST) {
  32. // Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
  33. if (doesAddToastWindowRequireToken(attrs.packageName, callingUid,
  34. parentWindow)) {
  35. Slog.w(TAG_WM, "Attempted to add a toast window with unknown token "
  36. + attrs.token + ". Aborting.");
  37. return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
  38. }
  39. }
  40. final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
  41. token = new WindowToken(this, binder, type, false, displayContent,
  42. session.mCanAddInternalSystemWindow);//
  43. } else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {//
  44. atoken = token.asAppWindowToken();//
  45. if (atoken == null) {
  46. Slog.w(TAG_WM, "Attempted to add window with non-application token "
  47. + token + ". Aborting.");
  48. return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
  49. } else if (atoken.removed) {
  50. Slog.w(TAG_WM, "Attempted to add window with exiting application token "
  51. + token + ". Aborting.");
  52. return WindowManagerGlobal.ADD_APP_EXITING;
  53. }
  54. } else if (rootType == TYPE_INPUT_METHOD) {
  55. if (token.windowType != TYPE_INPUT_METHOD) {
  56. Slog.w(TAG_WM, "Attempted to add input method window with bad token "
  57. + attrs.token + ". Aborting.");
  58. return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
  59. }
  60. }
  61. ...

注释1处通过displayContent的getWindowToken方法来得到WindowToken。注释2处,如果有父窗口就将父窗口的type值赋值给rootType,如果没有将当前窗口的type值赋值给rootType。接下来如果WindowToken为null,则根据rootType或者type的值进行区分判断,如果rootType值等于TYPE_INPUT_METHOD、TYPE_WALLPAPER等值时,则返回状态值WindowManagerGlobal.ADD_BAD_APP_TOKEN,说明rootType值等于TYPE_INPUT_METHOD、TYPE_WALLPAPER等值时是不允许WindowToken为null的。通过多次的条件判断筛选,最后会在注释3处隐式创建WindowToken,这说明当我们添加窗口时是可以不向WMS提供WindowToken的,前提是rootType和type的值不为前面条件判断筛选的值。WindowToken隐式和显式的创建肯定是要加以区分的,注释3处的第4个参数为false就代表这个WindowToken是隐式创建的。接下来的代码逻辑就是WindowToken不为null的情况,根据rootType和type的值进行判断,比如在注释4处判断如果窗口为应用程序窗口,在注释5处会将WindowToken转换为专门针对应用程序窗口的AppWindowToken,然后根据AppWindowToken的值进行后续的判断。

addWindow方法part3

  1. ...
  2. final WindowState win = new WindowState(this, session, client, token, parentWindow,
  3. appOp[], seq, attrs, viewVisibility, session.mUid,
  4. session.mCanAddInternalSystemWindow);//
  5. if (win.mDeathRecipient == null) {//2
  6. // Client has apparently died, so there is no reason to
  7. // continue.
  8. Slog.w(TAG_WM, "Adding window client " + client.asBinder()
  9. + " that is dead, aborting.");
  10. return WindowManagerGlobal.ADD_APP_EXITING;
  11. }
  12.  
  13. if (win.getDisplayContent() == null) {//
  14. Slog.w(TAG_WM, "Adding window to Display that has been removed.");
  15. return WindowManagerGlobal.ADD_INVALID_DISPLAY;
  16. }
  17.  
  18. mPolicy.adjustWindowParamsLw(win.mAttrs);//
  19. win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
  20. res = mPolicy.prepareAddWindowLw(win, attrs);//
  21. ...
  22. win.attach();
  23. mWindowMap.put(client.asBinder(), win);//
  24. if (win.mAppOp != AppOpsManager.OP_NONE) {
  25. int startOpResult = mAppOps.startOpNoThrow(win.mAppOp, win.getOwningUid(),
  26. win.getOwningPackage());
  27. if ((startOpResult != AppOpsManager.MODE_ALLOWED) &&
  28. (startOpResult != AppOpsManager.MODE_DEFAULT)) {
  29. win.setAppOpVisibilityLw(false);
  30. }
  31. }
  32.  
  33. final AppWindowToken aToken = token.asAppWindowToken();
  34. if (type == TYPE_APPLICATION_STARTING && aToken != null) {
  35. aToken.startingWindow = win;
  36. if (DEBUG_STARTING_WINDOW) Slog.v (TAG_WM, "addWindow: " + aToken
  37. + " startingWindow=" + win);
  38. }
  39.  
  40. boolean imMayMove = true;
  41. win.mToken.addWindow(win);//
  42. if (type == TYPE_INPUT_METHOD) {
  43. win.mGivenInsetsPending = true;
  44. setInputMethodWindowLocked(win);
  45. imMayMove = false;
  46. } else if (type == TYPE_INPUT_METHOD_DIALOG) {
  47. displayContent.computeImeTarget(true /* updateImeTarget */);
  48. imMayMove = false;
  49. } else {
  50. if (type == TYPE_WALLPAPER) {
  51. displayContent.mWallpaperController.clearLastWallpaperTimeoutTime();
  52. displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
  53. } else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != ) {
  54. displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
  55. } else if (displayContent.mWallpaperController.isBelowWallpaperTarget(win)) {
  56. displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
  57. }
  58. }
  59. ...

在注释1处创建了WindowState,它存有窗口的所有的状态信息,在WMS中它代表一个窗口。从WindowState传入的参数,可以发现WindowState中包含了WMS、Session、WindowToken、父类的WindowState、LayoutParams等信息。紧接着在注释2和3处分别判断请求添加窗口的客户端是否已经死亡、窗口的DisplayContent是否为null,如果是则不会再执行下面的代码逻辑。注释4处调用了WMP的adjustWindowParamsLw方法,该方法的实现在PhoneWindowManager中,会根据窗口的type对窗口的LayoutParams的一些成员变量进行修改。注释5处调用WMP的prepareAddWindowLw方法,用于准备将窗口添加到系统中。
注释6处将WindowState添加到mWindowMap中。注释7处将WindowState添加到该WindowState对应的WindowToken中(实际是保存在WindowToken的父类WindowContainer中),这样WindowToken就包含了相同组件的WindowState。

addWindow方法总结

addWindow方法分了3个部分来进行讲解,主要就是做了下面4件事:

  1. 对所要添加的窗口进行检查,如果窗口不满足一些条件,就不会再执行下面的代码逻辑。
  2. WindowToken相关的处理,比如有的窗口类型需要提供WindowToken,没有提供的话就不会执行下面的代码逻辑,有的窗口类型则需要由WMS隐式创建WindowToken。
  3. WindowState的创建和相关处理,将WindowToken和WindowState相关联。
  4. 创建和配置DisplayContent,完成窗口添加到系统前的准备工作。

结语

在本篇文章中我们首先学习了WMS的重要成员,了解这些成员有利于对WMS的进一步分析。接下来我们又学习了Window的添加过程的WMS部分,将addWindow方法分为了3个部分来进行讲解,从addWindow方法我们得知WMS有3个重要的类分别是WindowToken、WindowState和DisplayContent,关于它们会在本系列后续的文章中进行介绍。

 

Android解析WindowManagerService(二)WMS的重要成员和Window的添加过程的更多相关文章

  1. Android解析WindowManagerService(三)Window的删除过程

    前言 在本系列文章中,我提到过:Window的操作分为两大部分,一部分是WindowManager处理部分,另一部分是WMS处理部分,Window的删除过程也不例外,本篇文章会介绍Window的删除过 ...

  2. Android解析WindowManager(三)Window的添加过程

    前言 在此前的系列文章中我们学习了WindowManager体系和Window的属性,这一篇我们接着来讲Window的添加过程.建议阅读此篇文章前先阅读本系列的前两篇文章. 1.概述 WindowMa ...

  3. Android窗口系统第二篇---Window的添加过程

    以前写过客户端Window的创建过程,大概是这样子的.我们一开始从Thread中的handleLaunchActivity方法开始分析,首先加载Activity的字节码文件,利用反射的方式创建一个Ac ...

  4. Android解析WindowManagerService(一)WMS的诞生

    前言 此前我用多篇文章介绍了WindowManager,这个系列我们来介绍WindowManager的管理者WMS,首先我们先来学习WMS是如何产生的.本文源码基于Android 8.0,与Andro ...

  5. Android Touch事件传递机制全面解析(从WMS到View树)

    转眼间近一年没更新博客了,工作一忙起来.非常难有时间来写博客了,因为如今也在从事Android开发相关的工作,因此以后的博文也会很多其它地专注于这一块. 这篇文章准备从源代码层面为大家带来Touch事 ...

  6. Android图片加载框架最全解析(二),从源码的角度理解Glide的执行流程

    在本系列的上一篇文章中,我们学习了Glide的基本用法,体验了这个图片加载框架的强大功能,以及它非常简便的API.还没有看过上一篇文章的朋友,建议先去阅读 Android图片加载框架最全解析(一),G ...

  7. Android解析WindowManager(二)Window的属性

    前言 在上一篇文章我们学习了WindowManager体系,了解了Window和WindowManager之间的关系,这一篇我们接着来学习Window的属性. 1.概述 上一篇文章中我们讲过了Wind ...

  8. 【转载】C++ 与“类”有关的注意事项总结(十二):按成员初始化 与 按成员赋值

    原文:C++ 与"类"有关的注意事项总结(十二):按成员初始化 与 按成员赋值 一.按成员初始化(与构造函数和拷贝构造函数有关) 用一个类对象初始化另一个类对象,比如: Accou ...

  9. android利用zbar二维码扫描-(解决中文乱码及扫描区域定义)

    写在最前(这是对上一篇博文的问题做的更新[android利用zbar二维码扫描]) project下载   zbarLib编译project  project下载0积分 bug 在2.3的系统中Hol ...

随机推荐

  1. json兼容ie8

    今天遇到一个问题,后台传递过来的json对象,在前端解析的时候用JSON.parse(result)方法不好使,查了一下是因为ie浏览器的问题.然后在网上翻了翻,找到了这个办法,可以使这个函数在ie中 ...

  2. dubbo学习笔记:快速搭建

    搭建一个简单的dubbo服务 参考地址: dubbo官网:http://dubbo.apache.org/zh-cn/docs/user/references/registry/zookeeper.h ...

  3. 【Sublime】Sublime插件

    alignmentcodecs33convertToUtf8sublimeAstyleFormattersublimeLintersublimeLInter-contrib-clangtagInput ...

  4. php7安装并apache

    1.下载php7 选择THREAD SAFE版本,如果是64位系统要下载x64的,x86的不行 2.解压到想要安装的目录 3.apache配置 1) 添加PHP模块 查找“Dynamic Shared ...

  5. windows7用WMware安装Linux虚拟机详细步骤

    一.安装环境 windows7操作系统物理机VMware Workstation 软件(可以在网上下载)CentOS6.5镜像文件(其他版本都大同小异,这里以CentOS6.5为例)Cnetos6.5 ...

  6. 关于dubbo调度时出现Request processing failed; nested exception is com.alibaba.dubbo.rpc.RpcException: Failed to invoke the method insertTestTb in the service cn.cuibusi.core.service.TestTbService.的解决办法

    在用dubbo跨项目调度service时出现如下错误: 错误原因:pojo没有实现序列化 解决方法:在pojo实现序列化接口即可

  7. 关于docker的理解随记

    1.容器其实不是什么新技术,说白了就是namespace对资源进行隔离,再加UFS实现分层镜像,以及cgroup实现资源限制.这些技术,都是linux中已有的技术,而且有些技术很早之前就有了. 2.上 ...

  8. session_start()导致history.go(-1)返回时无法保存表单数据的解决方法

    问题背景: 在填写完表单提交时,由于某个表单项可能填写的不合法,导致提交失败,返回表单页面.但返回后所有的表单都被清空了,重新填写比较麻烦,度娘解释说,是由于每个页面都调用了session_start ...

  9. SOAP1.1 and SOAP1.2

    在用cxf 做webservice客户端的时候碰到的: javax.xml.ws.soap.SOAPFaultException: A SOAP 1.2 message is not valid wh ...

  10. Python(1):入门

    安装: 在linux中一般都自带有python2.7的版本,如果想升级python到最新的版本可以参考其他博客(http://www.cnblogs.com/lanxuezaipiao/archive ...