WindowManager
的重要程度不需要多说, 界面真正的显示, 外部事件的传递等. 这是一个子系统. 本章会梳理WindowManager
,WindowManagerService
,Surface
,SurfaceFlinger
之间的联系与交互的一个过程.
如果想了解不同的window级别的区别,如Activity,Dialog,Toast等请看另一篇blog
在此之前需要知道这么几件事情:
- 我们设置的
View
视图并不能直接显示在界面上. 而是需要依附在Window
窗口上. 通过WindowManager
操作才可以显示. Window
是一个抽象的概念, 它可以说以DecorView
的形式表现其存在.- 代码层面
Window
是一个抽象类, 具体实现类是PhoneWindow
应用WindowManager的由来
对于重要的系统服务在手机开机的时候进行初始化, 然后保存在了系统进程
中. 当我们应用启动的时候会触发ContextImpl
类的加载, 在类加载的时候通过静态代码块
对各个系统服务进行进行了注册,并保存到一个静态map容器
中, 之后就可以通过getSystemService(serviceName)
的形式获取不同的系统服务.
WM与Window的关联
那么当创建一个Dialog
, 就需要创建一个Window
, 并把Window
与WM
进行关联. 那么可以直接看看Dialog的构造函数
是如何做的.
|
关联的代码已经看到, 可以看一下内部调用顺序
|
最后创建了一个WindowManagerImpl
对象, 与ContextImpl
注册WindowManagerImpl
不同的是, 这里多了一个参数parentWindow
. 这也就说此时构建的WindowManagerImpl
对象是与具体的Window
关联的,
刚才说到了WindowManagerImpl
类. 看一下这个类的大体你会发现. 这个类没有具体的逻辑处理, 而是把所有的逻辑利用桥接模式
转给了WindowMangerGlobal
这个对象的对应方法. 例如addView()
,removeViewImmediate()
,updateViewLayout()
等.
View的显示过程
刚才说过WindowManager
的实现类WindowManagerImpl
只是通过桥接模式
将事件传递给了WindowManagerGlobal
. 那么对应的addView()
逻辑肯定也在WindowManagerGlobal
中.
|
上面的代码中出现了一个ViewRootImpl
对象, 不要认为它是一个View
! 它继承自Handler
类,是native层和java层View系统通信的桥梁
. 例如performTraversals()
就是收到系统绘制的View消息后, 通过调用是视图树的各个节点的绘制流程方法如measure
,layout
,draw
来完成整棵树的视图.
WMS
是运行在Native层
的, 而现在只是在Framewrok层
. 那么之间的是如何建立联系的?
Native和Framework的连接
既然ViewRootImpl
是通信的桥梁, 那么就看看其构造函数的创建.
|
继续看看getWinSession()
这个重要的函数
|
getWindowManagerService()
函数首先获取了IWindowManager
对象, 本质就是一个远程对应的Binder
对象. 而远程WMS的Binder
获取是通过ServiceManager.getService()
实现的.
这里就可以总结为: Android Framework
与WMS
之间的通信也是通过Binder
机制进行的. 这里就建立了与WMS
的通信. 最后通过openSession()
函数来与WMS
建立一个通信会话, 相当于建立了一个长期的处理中心
, 双方有什么需要够可以通过这个Session
来交换信息.
但是! 此时不管是Dialog
或者Activity
的View还没有显示在手机屏幕上, WMS
只是负责管理手机屏幕上的View的z-order
, 也就是WMS
管理当前状态下哪一个View
应该在最上层显示. 所以WMS
管理的并不是Window
而是View
, 只不过它管理的是属于某个Window
下的View
.
之前分析ViewRootImpl
的构造函数结束了, 继续回View
的显示问题上. 当与WMS
建立Session
之后就会调用ViewRootImpl#setView()
函数, 该方法会向WMS
发起显示Dialog
或者Activity的DecorView
的请求.
setView()
函数这里只关心两步:
requestLayout()
: 请求布局- 通过
WindowSession#add()
向WMS
发起显示Window
的请求
先看一个请求布局的方法
|
在scheduleTraversals()
发送消息之后, 就会触发整个视图树的绘制操作, 最终会触发执行performTraversals()
. 这个函数主要做的操作如下:
- 获取
Surface
对象, 用于图形绘制 - 测量整个视图树的各个
View
的大小, 触发performMeasure()
- 布局整个视图树, 触发
performLayout()
- 绘制整颗视图树, 触发
performDraw()
在第四步中, Framework
会获取到图形绘制表面Surface
对象, 然后获取它的可绘制区域, 也就是常用的Canvas
对象, 然后Framework
在这个Canvas
对象上绘制. 而在调用过程中通过draw()
方法主要做了如下几个事情:
- 判断是使用
CPU
还是GPU
来进行绘制 - 获取绘制表面
Surface
对象 - 通过
Surface
对象获取并且锁住Canvas
绘图对象 - 从
DecorView
开始发起整颗视图树的绘制流程 Surface
对象解锁Canvas
, 并且通知SurfaceFlinger
当内容绘制完毕之后, 请求WMS
显示该窗口上的内容, 至此,Activity``Dialog
等组件的View
就显示到了屏幕上. 这里整理的只是比较高层次的整体脉络. 整个WMS
系统是即为复杂的, 涉及的概念和技术很是很多方面. 比如Surface
等.
最后提出<Android源码设计模式解析与实战>
一书的59页简化结构图