Android底层分析(二)——WindowManager
Android底层分析(二)——WindowManager
在了解WindowManagerService之前,先来认识一下它的其他伙伴们
¶1. WindowManager概述
android\view\WindowManager.java
WindowManager本身是一个接口,并且继承自ViewManager
ViewManager的内容比较简单,就是三件套
android\view\ViewManager.java
这三个方法分别对应了View的添加、更新和删除
并且这些方法都传入View作为参数,因此Window正是借助于View的形式得以展示
既然WindowManager继承了ViewManager接口,自然也就继承了这些方法,可用于对于View进行操作
android\view\WindowManager.java
除此以外,基于Window的特性,又加入了getDefaultDisplay()和removeViewImmediate()这两个方法
第一个getDefaultDisplay()返回的是WindowManager将创建Window的Display对象
而第二个removeViewImmediate()则是removeView()的一种变体,能够在返回前立即调用给定View的层级的View#onDetachedFromWindow()方法,这不适用于正常的应用销毁流程
¶2. Window概述
Window是一个抽象类,正如注释所写,它唯一的实现类是PhoneWindow
Window主要的职责是作为View的容器,以及定义标准UI的一些基础规范
android\view\Window.java
简要阐述一下Window在何时进行实例化
这里直接来到Activity的代码中,在启动Activity时,会调用到attach(),此时,Window对象进行实例化
android\app\Activity.java
同样在attach()中,下面又调用了setWindowManager()方法,该方法定义在Window中

在WindowManager为空时,会调用Context的getSystemService()方法进行获取,这里传入服务名称Context.WINDOW_SERVICE
android\view\Window.java

该服务的名称就是window
android\content\Context.java

ContextImpl覆写了Context的getSystemService()方法,这里涉及到装饰器模式
android\app\ContextImpl.java
进而,调用SystemServiceRegistry的getSystemService(),SYSTEM_SERVICE_FETCHERS是一个ArrayMap结构,从中获取对应服务的ServiceFetcher
android\app\SystemServiceRegistry.java
ServiceFetcher作为SystemServiceRegistry的内部接口,通过getService()拿取服务,注意这里有缓存创建,CachedServiceFetcher实现了ServiceFetcher接口
继续看下面的代码,如果有对应的服务缓存,直接返回;如果是第一次,会去调用createService()方法
在SystemServiceRegistry类的static{}中注册了对应服务,createService()的抽象方法被覆写,最终作为服务返回的是WindowManagerImpl对象

那么,取出的WindowManagerImpl被转为WindowManager,回到Window.java,下面又转回来
android\view\Window.java

调用createLocalWindowManager()实质上是依据传入的Window对象new了一个WindowManagerImpl对象
android\view\WindowManagerImpl.java

此时,新的WindowManagerImpl对象便持有了原来Window的引用,那么通过Window#getWindowManager()可以获得操作Window的权力,因为这正好获取了Window中的mWindowManager成员,实际的功能还是依赖于WindowManagerImpl

但是,在WindowManagerImpl会发现,其实WindowManagerImpl也是把任务交给了mGlobal来做
mGlobal是WindowManagerGlobal的单例对象(WindowManagerGlobal进程唯一,但是WindowManagerImpl的实例一个进程有可能有多个),这里用到了桥接模式

¶3. 关系图
简要描述下操作Window前都做了些什么

其次,看下一些主要类的层次结构关系,仅标注一些主要的内容
¶4. 属性
Window的属性有很多种,其中最常用到的主要有三种:
- Type(类型)
- Flag(标志位)
- SoftInputMode(软键盘模式)
¶4.1. Window的类型以及显示的次序
Window的主要类型有以下三种:
- 常规的应用程序窗口
- 子窗口
- 系统专用窗口
¶4.1.1. 常规的应用程序窗口
比如说Activity就属于这个范畴
对应的type数值为1~99
¶4.1.2. 子窗口
这种窗口类型无法独立存在,需要依附在其他窗口之上
如PopWindow就属于该范畴
对应的type数值为1000~1999
¶4.1.3. 系统专用窗口
Toast、软键盘、系统错误弹窗等窗口属于该范畴
系统专用窗口的类型还是比较多的,其对应type为2000~2999
¶4.1.4. 窗口显示次序
进程向WMS申请窗口进行显示,WMS为窗口确定显示的次序
正如CSS里面的z-index属性一样,在垂直于屏幕的方向上,Android也提供Z-Order的概念,用于表示页面显示层级
前面提到的Window对应的type正好就是Z-Order进行排序的依据
一般情况下,type的值越大,那么窗口显示的层级越靠上,也就越接近用户
¶4.2. 标志位
flag的主要用途是赋予窗口某些特性,比如不可接收触摸事件、触发亮屏等
¶4.3. 软键盘模式
主要用于用户输入时,页面与软键盘弹窗叠加时的显示模式,这个很大程度上会影响用户的交互体验,现实场景中其实很重要
¶5. 操作
对于Window的操作可以分为两个部分:
WindowManager的处理WMS的处理
¶5.1. 系统窗口的添加(状态栏为例)
在StatusBar#start()中,createAndAddWindows()所做的就是构建StatusBar的视图,并且将其添加到WindowManager上
com\android\systemui\statusbar\phone\StatusBar.java

其中,makeStatusBarView()所做的就是构建视图

然后,接下来StatusBarWindowController#attach()将对应的视图添加到WindowManager上,同时也可以看到类型参数TYPE_STATUS_BAR
com\android\systemui\statusbar\phone\StatusBarWindowController.java

调用addView()进行添加,这还是调用了WindowManagerImpl的方法,传入对应View参数
android\view\WindowManagerImpl.java

作为子窗口时,adjustLayoutParamsForSubWindow()调整窗口的大小,适配当前的父窗口
android\view\WindowManagerGlobal.java

这里先看看类开头定义的3个参数,mViews、mRoots、mParams,都是ArrayList

随后接着看addView(),实例化ViewRootImpl对象,为View设置窗口参数,然后三个列表对应保存数据,然后将设置完的View塞给ViewRootImpl对象

ViewRootImpl承担了很多职责,比如View树管理,测量、布局、绘制,与WMS通信等
这里调用addToDisplayAsUser(),mWindowSession是一个Binder对象,是IWindowSession类型,用以进行进程间通信,IWindowSession是客户端代理,与之对应的服务端实现为Session,Session的addToDisplayAsUser()方法运行在WMS所在进程(system_server)
android\view\ViewRootImpl.java

接受到通信,Session的addToDisplayAsUser()被调用,Session被作为参数,WMS调用addWindow(),将其传入,剩下的工作都交由WMS处理了
com\android\server\wm\Session.java


¶5.2. Activity添加过程
performResumeActivity()最终会调用Activity的onResume(),然后getWindowManager(),这是之前的,返回的其实是WindowManagerImpl
android\app\ActivityThread.java

调用addView(),之后和其他的Window添加过程就很类似了,这里传入的是DecorView

¶5.3. Window更新
更新过程的起点从ViewManager的updateViewLayout()开始,实际上调用了WindowManagerImpl的updateViewLayout()
android\view\WindowManagerImpl.java

然后,也是由WindowManagerGlobal管事,同样是操作三个列表,调用ViewRootImpl的setLayoutParams()
android\view\WindowManagerGlobal.java

在ViewRootImpl的setLayoutParams()最后调用了scheduleTraversals()
android\view\ViewRootImpl.java

如果使用过SysTrace这类工具应该很熟悉这个名字,Choreographer,这里与UI绘制开始关联上了,其主要任务是接受显示系统的VSync信号,在下一帧渲染时进行一些同步操作
postCallback()发送了一个线程任务TraversalRunnable

字面上看,叫“遍历线程”,它的线程任务为doTraversal()

所做的主要事情都在performTraversals()中

performTraversals()中的代码量非常大,都是与View绘制相关的,所谓的遍历其实就是遍历视图树
首先,relayoutWindow()实际会去调用mWindowSession.relayout(),这又是一个Binder通信,和之前一样,又是WMS去处理

随后,是自定义View需要了解的三连,也就是View的工作流程,这几个方法分别对应了View的onMeasure()、onLayout()、onDraW()



一方面通知WMS去更新,另一方面进行View的绘制,完成了Window的更新




