简析Window、Activity、DecorView以及ViewRoot之间的错综关系
关于Android中View视图的呈现,我们平常看到最多的就是在Activity中通过
setContentView(R.layout.activity_main);设置来显示。然而其实视图的显示并不是由Activity来完成的,其中涉及到了Window、DecorView、ViewRoot。四者之间关系交互复杂,共同完成视图的显示以及与用户之间的交互。本篇文章简单介绍四者的各自职能,以及视图的显示过程。由于程度有限,认识可能不到位,欢迎指正。
一、职能简介
Activity
Activity并不负责视图控制,它只是控制生命周期和处理事件。真正控制视图的是Window。一个Activity包含了一个Window,Window才是真正代表一个窗口。Activity就像一个控制器,统筹视图的添加与显示,以及通过其他回调方法,来与Window、以及View进行交互。
Window
Window是视图的承载器,内部持有一个 DecorView,而这个DecorView才是 view 的根布局。Window是一个抽象类,实际在Activity中持有的是其子类PhoneWindow。PhoneWindow中有个内部类DecorView,通过创建DecorView来加载Activity中设置的布局R.layout.activity_main。Window 通过WindowManager将DecorView加载其中,并将DecorView交给ViewRoot,进行视图绘制以及其他交互。
DecorView
DecorView是FrameLayout的子类,它可以被认为是Android视图树的根节点视图。DecorView作为顶级View,一般情况下它内部包含一个竖直方向的LinearLayout,在这个LinearLayout里面有上下三个部分,上面是个ViewStub,延迟加载的视图(应该是设置ActionBar,根据Theme设置),中间的是标题栏(根据Theme设置,有的布局没有),下面的是内容栏。具体情况和Android版本及主体有关,以其中一个布局为例,如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
在Activity中通过setContentView所设置的布局文件其实就是被加到内容栏之中的,成为其唯一子View,就是上面的id为content的FrameLayout中,在代码中可以通过content来得到对应加载的布局。
ViewGroup content = (ViewGroup)findViewById(android.R.id.content);
|
ViewRoot
ViewRoot可能比较陌生,但是其作用非常重大。所有View的绘制以及事件分发等交互都是通过它来执行或传递的。
ViewRoot对应ViewRootImpl类,它是连接WindowManagerService和DecorView的纽带,View的三大流程(测量(measure),布局(layout),绘制(draw))均通过ViewRoot来完成。
ViewRoot并不属于View树的一份子。从源码实现上来看,它既非View的子类,也非View的父类,但是,它实现了ViewParent接口,这让它可以作为View的名义上的父视图。RootView继承了Handler类,可以接收事件并分发,Android的所有触屏事件、按键事件、界面刷新等事件都是通过ViewRoot进行分发的。
下面结构图可以清晰的揭示四者之间的关系:
二、DecorView的创建
这部分内容主要讲DecorView是怎么一层层嵌套在Actvity,PhoneWindow中的,以及DecorView如何加载内部布局。
setContentView
先是从Activity中的setContentView()开始。
public void setContentView(@LayoutRes int layoutResID) {
|
可以看到实际是交给Window装载视图。下面来看看Activity是怎么获得Window对象的?
final void attach(Context context, ActivityThread aThread,
|
在Activity中的attach()方法中,生成了PhoneWindow实例。既然有了Window对象,那么我们就可以设置DecorView给Window对象了。
public void setContentView(int layoutResID) {
|
看了下来,可能有一个疑惑:mContentParent到底是什么?
就是前面布局中@android:id/content所对应的FrameLayout。
通过上面的流程我们大致可以了解先在PhoneWindow中创建了一个DecroView,其中创建的过程中可能根据Theme不同,加载不同的布局格式,例如有没有Title,或有没有ActionBar等,然后再向mContentParent中加入子View,即Activity中设置的布局。到此位置,视图一层层嵌套添加上了。
下面具体来看看installDecor();方法,怎么创建的DecroView,并设置其整体布局?
private void installDecor() {
|
再来看看 generateDecor()
protected DecorView generateDecor() {
|
很简单,创建了一个DecorView。
再看generateLayout
protected ViewGroup generateLayout(DecorView decor) {
|
虽然比较复杂,但是逻辑还是很清楚的。先从主题中获取样式,然后根据样式,加载对应的布局到DecorView中,然后从中获取mContentParent。获得到之后,可以回到上面的代码,为mContentParent添加View,即Activity中的布局。
以上就是DecorView的创建过程,其实到installDecor()就已经介绍完了,后面只是具体介绍其中的逻辑。
三、DecorView的显示
以上仅仅是将DecorView建立起来。通过setContentView()设置的界面,为什么在onResume()之后才对用户可见呢?
这就要从ActivityThread开始说起了,由于个人也是一知半解,仅仅阐述下个人理解吧。
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
|
重点看下handleResumeActivity(),在这其中,DecorView将会显示出来,同时重要的一个角色:ViewRoot也将登场。
final void handleResumeActivity(IBinder token,
|
当我们执行了Activity.makeVisible()方法之后,界面才对我们是可见的。
void makeVisible() {
|
到此DecorView便可见,显示在屏幕中。但是在这其中,wm.addView(mDecor, getWindow().getAttributes());起到了重要的作用,因为其内部创建了一个ViewRootImpl对象,负责绘制显示各个子View。
具体来看addView()方法,因为WindowManager是个接口,具体是交给WindowManagerImpl来实现的。
public final class WindowManagerImpl implements WindowManager {
|
交给WindowManagerGlobal 的addView()方法去实现
public void addView(View view, ViewGroup.LayoutParams params,
|
看到其中实例化了ViewRootImpl对象,然后调用其setView()方法。其中setView()方法经过一些列折腾,最终调用了performTraversals()方法,然后依照下图流程层层调用,完成绘制,最终界面才显示出来。
其实ViewRootImpl的作用不止如此,还有许多功能,如事件分发。
要知道,当用户点击屏幕产生一个触摸行为,这个触摸行为则是通过底层硬件来传递捕获,然后交给ViewRootImpl,接着将事件传递给DecorView,而DecorView再交给PhoneWindow,PhoneWindow再交给Activity,然后接下来就是我们常见的View事件分发了。
硬件 -> ViewRootImpl -> DecorView -> PhoneWindow -> Activity
不详细介绍了,如果感兴趣,可以看这篇文章。
由此可见ViewRootImpl的重要性,是个连接器,负责WindowManagerService与DecorView之间的通信。
四、总结
以上通过源码形式介绍了Window、Activity、DecorView以及ViewRoot之间的错综关系,以及如何创建并显示DecorView。
通过以上了解可以知道,Activity就像个控制器,不负责视图部分。Window像个承载器,装着内部视图。DecorView就是个顶层视图,是所有View的最外层布局。ViewRoot像个连接器,负责沟通,通过硬件的感知来通知视图,进行用户之间的交互。
参考文章:
Android View源码解读:浅谈DecorView与ViewRootImpl
window、Activity、DecorView、ViewRoot关系
Android 源码解析 之 setContentView
浅析 android 应用界面的展现流程(二)布局与视图的创建
【凯子哥带你学Framework】Activity界面显示全解析(下)


浙公网安备 33010602011771号