view原理(二)——DecorView和ViewRoot
如前面所讲,如果每一个View都有一个父View,那么一直向上面找,最上面或者说顶级的那个View有没有父View呢?总是要有一个View是没有父View的,那么这个View就是DecorView。
DecorView
运行代码看效果,打开DDMS,perviews选项卡(重叠的小手机图标),我们就可以看到我们当前应用的视图层级,这其中不包括状态栏(扩展:状态栏属于另外一个系统级应用,不在我们的应用范围内,所以我们不能看到其视图层级;状态栏只有两种状态,一种是收起来的通知栏形态,一种是拉下来的全屏状态)。
DecorView做两件事情:它把界面分成两部分,第一部分放ActionBar,第二部分放我们自己在调用setContentView()方法时所设置的布局。在android4.0之前,没有ActionBar的时候,上面一部分叫title.在4.0以后不叫title,叫ActionBar了。如果不想显示title或者ActionBar,ActionBar还好一点,如果不想显示title,必须要在调用setContentView()方法之前通过Window对象设置一个Flag,告诉系统不要标题。
为什么要在调用setContentView()方法之前?因为一旦set的时候,系统就会检查根View存不存在Activity里,如果不存在会去创建根View,接着检查Flag,需不需要创建标题,如果这时候你没设置,系统会默认为你需要创建标题就会去创建,所以要在之前调用。
DecorView的父容器:ViewRoot
看过ViewGroup的源码我们知道一个View可以成为ViewGroup的要点是实现两个接口,而不是继承View,所以根View的父亲只需要实现两个接口即可,而不需要继承自View即可承装根View。所以根View的父容器不是一个View。而实际上DecorView的父容器是Handler,它实现了ViewParent这个接口,因此可以承装DecorView。
我们知道Handler是跟主线程通信的,往主线程存放任务的发送任务到主线程,让主线程去执行。我们UI的一些绘制全部都要在主线程里面进行。系统就把根View的父容器设置为Handler。这样操作起来会比较方便。直接由它向主线程通信,下面所有的子view只要向上请求父View去操作,最后消息就会到Handler那里,Handler就会发送到主线程。
而在这里因Handler所起的作用,它有一个更适合的名字:ViewRoot。这个是android2.3版本之后的名字,但在4.0之后谷歌官方对ViewRoot进行了解耦,使它变成了一个抽象类,而这时代替它的叫做ViewRootImpl。因为目前也只有这一个实现类,所以说ViewRoot等同于ViewRootImpl。
ViewRoot的跨进程通信
ViewRoot还可以和windowManagerService(以下简称WMS)进行跨进程通信。出于安全考虑,android系统中的应用层并不能直接管理手机硬件,而是通过WMS进行管理,这是一个系统级的后台服务,所有需要对屏幕进行修改的都是通过它来管理。相对应地我们自己的应用中也有一个WindowManager来管理应用内部的Window对象。Window对象是对手机屏幕上的一个窗口进行的描述(手机屏幕有另外的描述,不是Window)。每一个Activity有且仅有一个Window对象。三者关系如图:
那么又是谁来通知WMS应该处理那个Window呢?就是ViewRoot。
在ViewRoot内部维护着两个Binder对象,一个用于处理DecorView的事件,另一个就与WMS进行请求。流程大致如下:
从此流程中可见,仅仅是一个View请求进行修改,但是却调用了很多资源,效率低。但是这样的流程也有好处:方便管理和架构,WMS不用考虑同步的问题。