Android笔记(四)DecorView & ViewRootImpl & Window

来源:互联网 发布:魔龙诀时装进阶数据 编辑:程序博客网 时间:2024/06/07 03:32

这三者之间的关系网络上有很多篇不错的blog。比如这篇。为了让自己更加熟悉View绘制的流程,在这里特意将这三者的关系好好梳理一番。
为了理清这三者的关系,还是先上一张图
这里写图片描述

  1. ViewRootImpl是ViewRoot的实现类,ViewRoot不是view,而是整个ViewTree的管理者。
  2. DecorView是整个ViewTree的根布局视图
  3. ViewRoot通过在Activity.attach()中将DecorView绑定到Activity对应的Window中,在addView时涉及到跨进程通信
  4. 这里的跨进程通信主要就是ViewRoot和WMS之间通信,通过传递IWindowSession和IWindow来完成。

按照个人理解,在window起来的时候,先完成的工作是得到一个最初的View,用作最底层的view(或者说放在Window里面作为根view,事实上不存在这种将view放置在window中的说法,因为window只是一个虚拟的窗口),这个view也就是我们通常说的DecorView。得到这个DecorView之后,接下来的工作就是通过addView将这个View呈现出来,这里才会涉及到ViewRootImpl,这样DecorView才算是真正与window绑定在一起了。

Window创建过程

PhoneWindow是Window的实现类,里面几个和DecorView相关的属性

ViewGroup mContentParent;//ViewGroup,也就是我们的R.id.contentTextView mTitleView;//也就是我们的R.id.titleDecorContentParent mDecorContentParent; //接口,用来设置一些window的属性

我们以Activity的启动创建Window为例来进行说明,除此之外Dialog、Toast等在启动时也会创建Window。
Activity的启动较为复杂,后面会专门花时间来讲解。Acitvity的启动最终都会由ActivityThread的performLaunchActivity()来完成启动。

...//通过反射创建AcitivityClassLoader cl = r.packageInfo.getClassLoader();activity=mInstrumentation.newActivity(cl,component.getClassName(),r.intent);...//在activity中创建window对象activity.attach(appContext,this,getInstrumentation(),r.token,..window);

在Activity中的attach方法中,完成了 Activity中window的创建,而且设置相关回调接口,包括我们常见的onAttachedToWindow、dispatchTouchEvent()等。此外还将ActivityThread中的信息传递到Activity

attachBaseContext();mWindow = new PhoneWindow(this,window);mWindow.setCallback(this);...mUiThread = Thread.currentThread();mMainThread = aThread;mInstrumentation = instr;mToken = token;

这样我们就在启动一个Acitivity时,创建好了一个window。但此时window中啥都没有,我们在activity中解析xml文件,添加view到window是怎样做到的呢。首先,我们需要想到的是,一个window对应一个根view。这个view就是DecorView。添加view的过程在setContentView()中完成。包括创建DecorView,添加view,显示view。
Activity中的setContentView()方法

public void setContentView(View view,ViewGroup.LayoutParams params){    getWindow().setContentView(view,params);    initWindowDecorActionBar();}

Window的具体实现是PhoneWindow。因此我们只需要看PhoneWindow中的相关逻辑。

在PhoneWindow的setContentView方法中,如果没有DecorView,那就创建一个。

//mContentParent是DecorView的子布局ViewGroup,也就是我们的R.id.contentif(mContentParent = null){    installDecor();}

然后解析我们的xml或者加载view到mContentParent上,其中mLayoutInflater在PhoneWindow构造时被赋值

mLayoutInflater.inflate(layoutResID,mContentParent);

完成这些后,还只是完成了Window的创建以及与Activity的绑定,还没有将Window添加到WMS中,所以还不会显示出来(mDecorView还没有被wms识别)。显示通过ActivityThread中的handleResumeActivity()来完成

ifa.mVisibleFromClient && !a.mWindowAdded){    a.mWindowAdded = true;    wm.addView(decor,l);}

DecorView介绍

DecorView为顶级View,事实上也具有普通View的特性,其布局为FrameLayout布局。以前的版本中,DecorView是PhoneWindow的内部类,PhoneWindow是Window抽象类的唯一实现,足以表示两者的关系。不过最新的版本中DecorView已经独立成了一个普通类,里面有三个最重要的属性
这里写图片描述

  • mWindow 窗口,后续加载content内容用到

  • mContentRoot 用于放置R.id.content的ViewGroup

  • mDecorCaptionView 和多窗口相关

这样我们就清楚地知道了DecorView里面的布局。对于普通Activity这两这个子View(mContentRoot mDecorCaptionView都有,对于没有多窗口的,则只有mContentRoot)。将view添加到DecorView主要是靠下面这个方法onResourceLoaded()
这里写图片描述

layoutResource为我们传进来的R.layout.xxx资源。先创建mDecorCaptionView
这里写图片描述
第1992行清楚地向我们显示了DecorCaptionView的加载。再回到上一幅图,加载完DecorCaptionView之后就加载mContentRoot(01919行所示),然后需要做的事情就是先判断mDecorCaptionView是否为空,如果为空则只把mContentRoot直接添加到DecorView中。如果不为空则先把mDecorCaptionView添加到DecorView,然后把mContentRoot添加到mDecorCaptionView中。大致的图如下
这里写图片描述
到这里我们就完成了整个DecorView的构造,下面就要进行其放置过程。事实上,DecorView的构造是在PhoneWindow中进行的。大概流程为:Activity中的setContentView最终会调用到PhoneWindow中的setContentView。在这个方法中首先就要判断DecorView是否存在,不存在就创建。DecorView的添加是在ActivityThread中的handlerResumeActivity添加的。

wm.addView(decor,l);

从这儿就可以看出Activity中onCreate在onResume方法之前,先创建DecorView,然后放置DecorView。WindowManager是一个抽象类,我们需要在其实现类WindowManagerImpl中去查看addview方法。结果发现真正调用的是WindowManagerGlobal类中的addView方法
这里写图片描述
也就是说,RootViewImpl和DecorView上下文一一对应。
这里写图片描述
从上面可以看出真正的添加DecorView操作是在这儿,调用ViewRootImpl.setView方法,在这个方法内部,会通过跨进程的方式向WMS(WindowManagerService)发起一个调用,从而将DecorView最终添加到Window上,在这个过程中,ViewRootImpl、DecorView和WMS会彼此关联,至于详细过程这里不展开来说了。最后通过WMS调用ViewRootImpl#performTraverals方法开始View的测量、布局、绘制流程。
后面将会讲解ViewTree遍历的时机

阅读全文
0 0
原创粉丝点击