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
的更新