简述
本篇文章还是偏应用层的主要说的是Activity的页面绘制,不过大家需要知道的是就算没有Activity也是可以绘制UI到手机屏幕上的,甚至不需要通过wms 例如手机的开机动画,因为往往开机的时候,wms服务并没有启动,开机动画由BootAnimation直接通过OpenGL ES 与 SurfaceFlinger来配合完成。
Activity的页面绘制的流程分析
Activity内部拥有完整的界面显示机制,是和其他三大组件最大的不同。完整的界面显示机制包含了ViewRootImpl,Window,以及它们管理的ViewTree等,简单说明一下几个重要部分之间的关系:
View和ViewRoot: 从字面意义上容易弄错,误认为ViewRoot是View树的根,其实不是的ViewRoot可以被理解为View树的管理者,它有一个mView的成员变量,该成员变量指向的就是它所管理的view树的根;ViewRoot的核心任务是和WindowManagerService进行通信。
Activity和Window的关系: Activity是支持UI显示的,但是Activity并不直接管理View树和ViewRoot,这之间存在Window。Activity内部有一个mWindow的成员变量,Window是基类,系统在Activity.attach中调用PolicyManager.makeNewWindow创建出Activity的window,Android系统默认生产的是PhoneWindow。
Window和WindowManagerImpl的关系: Window需要和WindowManagerService通信,但没有直接在自身实现这一功能。因为一个应用程序中可能存在多个Window,如果他们都单独与WindowManagerService通信这样容易造成管理上的混乱并且还浪费资源,为了统一管理于是有了WindowManager对应在Window中的成员变量mWinodwManager。WindowManager是一个接口类,真正的实现类是WindowManagerImpl。在4.3版本之前View树根节点数组,ViewRoot数组和Window属性数组是WindowManagerImpl的成员变量;而在4.3版本及以后三个数组都放在了WindowManagerGlobal中,而WindowManagerImpl则持有WindowManagerGlobal成员,WindowManagerGlobal以单例的方式提供。
ViewRoot和WindowManagerService的关系: 每一个ViewRootImpl都有一个全局变量 sWindowSession 这个变量用于ViewRoot到WindowManagerService的连接,是ViewRoot利用WindowManagerService的openSession()接口创建。在此基础上,ViewRoot也会通过IWindowSession.add()方法提供一个IWindow对象---从而让WindowManagerService也可以通过这个Binder对象来与ViewRoot进行双向通信。
下图是他们之间的关系
关系图如上图所示:每个应用都有一个ActivityThread主线程以及mActivitys全局变量,后者记录了运行在应用程序中的所有Activity对象。一个Activity对应唯一的WindowManager以及ViewRootImpl.WindowManagerGlobal作为全局管理者,其内部的mRoots和mViews记录了各Activity的ViewRootImpl和View树的顶层元素。ViewRootImpl的另一个重要角色就是负责与WindowManagerService通信。从ViewRootImpl到WindowManagerService间的通信利用的是IWindowSession,而反方向是由IWindow来完成的。
接下来我们从流程上才串一遍,先晒图:
流程图作为应用程序的主线程,ActivityThread负责处理各种核心事件。例如"AMS通知应用进程去启动一个Activity",最终会转化为ActivityThread所管理的LAUNCH_ACTIVITY消息,然后调用handleLaunchActivity,这是整个Activity页面绘制流程的起点。handlerLaunchActivity中会执行performLaunchActivity和handleResumeActivity两个重要方法。performLaunchActivity的源码如下:
ActivityThread.java这个方法的主要任务是生产一个Activity对象,并调用它的attach方法。然后通过callActivityOnCreate间接调用Activity.onCreate。其中attach将Activity内部众多全局变量赋值,最重要的是mWindow。创建mWindow的代码如下
mWindow = new PhoneWindow(this,window, activityConfigCallback);
Activity中得到的是一个PhoneWindow,它在每个Activity中有且仅有一个实例。Window在Activity中可以理解为界面的框架抽象,所以有了Window后接着会生成具体的View的内容,即Activity中的mDecor。产生DecorView的过程是由setContentView发起的,这个大家都经常接触,在Activity的onCreate方法中会执行setContentView;而onCreate本身则是由mInstrumentation.callActivityOnCreate(activity,r.state)间接调用的。
关于setContentView简单介绍一下,相关的会有mContentParent和DecorView。由于不同版本的代码有差异,但是不变的是得到mContentParent(是一个ViewGroup,用来容纳setContentView设置的布局)和创建DecorView(装饰布局);一般都会经过3个主要步骤:
1.取出Window样式,通过分析styleable.Window获得;
2.根据Window样式挑选符合要求的layout资源用layoutResource来表示,这些系统提供的layout文件统一存放在framework/base/core/res/res/layout下面
3.根据layoutResource指定的layout文件,来inflate出相应的View对象。然后把这一新对象addView到mDecor中(DecorView是一个ViewGroup继承FrameLayout),最后返回一个id为ID_ANDROID_CONTENT= com.android.internal.R.id.content的对象并赋值给mContentParent。最终展示的页面是我们通过setContentView设置的布局加上DecorView。需要注意的是setContentView并不负责将这一视图显示到页面上,只是把应用程序想要显示的视图加上系统策略中的其他元素结合起来(我们经常听到onCreate的时候页面还未显示,在onResume的时候才能看到页面这样的话)。
performLaunchActivity结束后,Activity内部已经完成了Window和DecorView创建的过程,但是外界还不可知;接下来需要将它产生的viewTree添加到WindowManagerGlobal中,继而注册到WMS里面。执行这一操作的代码在ActivityThread中的handleResumeActivity方法中,该方法会执行performResumeActivity最终会触发Activity的onResume方法。下面是handleResumeActivity的一些重点方法:
handleResumeActivity上图所示performResumeActivity会导致Activity的onResume被调用(在Activity的onResume方法中会设置DecorView的setVisibility方法为显示)。但是到目前为止我们还没有看到与WindowManagerService和SurfaceFlinger相关的内容,并不了解如何就显示到了界面上。上面的代码中还有一行关键 wm.addView(decor,l)。最终会执行WindowManagerGlobal的addView方法,在addView方法中创建了ViewRootImpl并将ViewTree,params,ViewRootImpl建立了一个关系,他们的关系是:分别会存储在WindowManagerGlobal中的mViews,mRoots,mParams三个数组中,同一个index数组下标下取出的三个数组的内容是描述同一个Activity中的ViewRoot,ViewTree,布局属性。
文章开头提到的ViewRootImpl 是可以和 WMS进行交互的。当WindowManagerGlobal中的addView执行后创建了ViewRootImpl,创建ViewRootImpl的时候通过WindowManagerService提供的openSession接口获取到了session通道,创建了一个IWindow对象。在addView方法的最后还执行了一个setView的方法,这个方法将ViewTree也就是setContentView生产的View的树根赋值给了mView,并且还向WMS申请了窗口,将创建的IWindow对象传给了WMS。建立了与WMS的通道主要目的是接受触发ViewTree的更新,例如窗口大小改变,输入窗口的事件,当然ViewTree也可以内部触发刷新,然后通过ViewRootImpl判断是否要上报给WMS;ViewRootImpl中有一个消息队列viewRootHandler,用来处理ViewTree的相关事件。
文章和部分图片参考-《深入理解Android内核设计思想》
网友评论