Android应用程序窗口(Activity)的视图对象(View)的创建过程分析
从前文可知道,每一个Activity组件都有一个关联的Window对象,用来描述一个应用程序窗口。每一个应用程序窗口内部又包含有一个View对象,用来描述应用程序窗口的视图。应用程序窗口视图是真正用来实现UI内容和布局的,也就是说,每一个Activity组件的UI内容和布局都是通过与其所关联的一个Window对象的内部的一个View对象来实现的。在本文中,我们就详细分析应用程序窗口视图的创建过程。
在前面Android应用程序窗口(Activity)实现框架简要介绍和学习计划一文中提到,应用程序窗口内部所包含的视图对象的实际类型为DecorView。DecorView类继承了View类,是作为容器(ViewGroup)来使用的,它的实现如图1所示:
图1 DecorView类的实现
这个图的具体描述可以参考Android应用程序窗口(Activity)实现框架简要介绍和学习计划一文中的图5,这里不再详述。
从前面Android应用程序窗口(Activity)实现框架简要介绍和学习计划一文还可以知道,每一个应用程序窗口的视图对象都有一个关联的ViewRoot对象,这些关联关系是由窗口管理器来维护的,如图2所示:
图2 应用程序窗口视图与ViewRoot的关系图
这个图的具体描述可以参考Android应用程序窗口(Activity)实现框架简要介绍和学习计划一文中的图6,这里不再详述。
简单来说,ViewRoot相当于是MVC模型中的Controller,它有以下职责:
1. 负责为应用程序窗口视图创建Surface。
2. 配合WindowManagerService来管理系统的应用程序窗口。
3. 负责管理、布局和渲染应用程序窗口视图的UI。
那么,应用程序窗口的视图对象及其所关联的ViewRoot对象是什么时候开始创建的呢? 从前面Android应用程序窗口(Activity)的窗口对象(Window)的创建过程分析一文可以知道,Activity组件在启动的时候,系统会为它创建窗口对象(Window),同时,系统也会为这个窗口对象创建视图对象。另一方面,当Activity组件被激活的时候,系统如果发现与它的应用程序窗口视图对象所关联的ViewRoot对象还没有创建,那么就会先创建这个ViewRoot对象,以便接下来可以将它的UI渲染出来。
从前面Android应用程序启动过程源代码分析一文可以知道,Activity组件在启动的过程中,会调用ActivityThread类的成员函数handleLaunchActivity,用来创建以及首次激活Activity组件,因此,接下来我们就从这个函数开始,具体分析应用程序窗口的视图对象及其所关联的ViewRoot对象的创建过程,如图3所示:
图3 应用程序窗口视图的创建过程
这个过程一共可以分为13个步骤,接下来我们就详细分析每一个步骤。
Step 1. ActivityThread.handleLaunchActivity
public final class ViewRoot extends Handler implements ViewParent, View.AttachInfo.Callbacks { ...... final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams(); ...... View mView; ...... final View.AttachInfo mAttachInfo; ...... boolean mAdded; ...... public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { mView = view; mWindowAttributes.copyFrom(attrs); ...... mAttachInfo.mRootView = view; ....... if (panelParentView != null) { mAttachInfo.mPanelParentWindowToken = panelParentView.getApplicationWindowToken(); } mAdded = true; ...... requestLayout(); ...... try { res = sWindowSession.add(mWindow, mWindowAttributes, getHostVisibility(), mAttachInfo.mContentInsets, mInputChannel); } catch (RemoteException e) { mAdded = false; mView = null; ...... throw new RuntimeException("Adding window failed", e); } finally { if (restore) { attrs.restore(); } } ...... } ...... } } ......}
这个函数定义在文件frameworks/base/core/java/android/view/ViewRoot.java中。
参数view所描述的一个View对象会分别被保存在ViewRoot类的成员变量mView以及成员变量mAttachInfo所描述的一个AttachInfo的成员变量mRootView中,而参数attrs所描述的一个WindowManager.LayoutParams对象的内容会被拷贝到ViewRoot类的成员变量mWindowAttributes中去。
当参数panelParentView的值不等于null的时候,就表示参数view描述的是一个子应用程序窗口视图对象。在这种情况下,参数panelParentView描述的就是一个父应用程序窗口视图对象。这时候我们就需要获得用来描述这个父应用程序窗口视图对象的一个类型为W的Binder本地对象的IBinder接口,以便可以保存在ViewRoot类的成员变量mAttachInfo所描述的一个AttachInfo的成员变量mPanelParentWindowToken中去。这样以后就可以知道ViewRoot类的成员变量mView所描述的一个子应用程序窗口视图所属的父应用程序窗口视图是什么了。注意,通过调用参数panelParentView的所描述的一个View对象的成员函数getApplicationWindowToken即可以获得一个对应的W对象的IBinder接口。
上述操作执行完成之后,ViewRoot类的成员函数setView就可以将成员变量mAdded的值设置为true了,表示当前正在处理的一个ViewRoot对象已经关联好一个View对象了。接下来,ViewRoot类的成员函数setView还需要执行两个操作:
1. 调用ViewRoot类的另外一个成员函数requestLayout来请求对应用程序窗口视图的UI作第一次布局。
2. 调用ViewRoot类的静态成员变量sWindowSession所描述的一个类型为Session的Binder代理对象的成员函数add来请求WindowManagerService增加一个WindowState对象,以便可以用来描述当前正在处理的一个ViewRoot所关联的一个应用程序窗口。
至此,我们就分析完成Android应用程序窗口视图对象的创建过程了。在接下来的一篇文章中,我们将会继续分析Android应用程序窗口与WindowManagerService服务的连接过程,即Android应用程序窗口请求WindowManagerService为其增加一个WindowState对象的过程,而在接下来的两篇文章中,我们还会分析用来渲染Android应用程序窗口的Surface的创建过程,以及Android应用程序窗口的渲染过程。通过这三个过程的分析,我们就可以看到上述第1点和第2点提到的两个函数的执行过程,敬请期待!