Android应用程序窗口(Activity)的绘图表面(Surface)的创建过程分析
在前文中,我们分析了应用程序窗口连接到WindowManagerService服务的过程。在这个过程中,WindowManagerService服务会为应用程序窗口创建过一个到SurfaceFlinger服务的连接。有了这个连接之后,WindowManagerService服务就可以为应用程序窗口创建绘图表面了,以便可以用来渲染窗口的UI。在本文中,我们就详细分析应用程序窗口的绘图表面的创建过程。
从前面Android应用程序与SurfaceFlinger服务的关系概述和学习计划和Android系统Surface机制的SurfaceFlinger服务简要介绍和学习计划这两个系列的文章可以知道,每一个在C++层实现的应用程序窗口都需要有一个绘图表面,然后才可以将自己的UI表现出来。这个绘图表面是需要由应用程序进程请求SurfaceFlinger服务来创建的,在SurfaceFlinger服务内部使用一个Layer对象来描述,同时,SurfaceFlinger服务会返回一个实现了ISurface接口的Binder本地对象给应用程序进程,于是,应用程序进程就可以获得一个实现了ISurface接口的Binder代理对象。有了这个实现了ISurface接口的Binder代理对象之后,在C++层实现的应用程序窗口就可以请求SurfaceFlinger服务分配图形缓冲区以及渲染已经填充好UI数据的图形缓冲区了。
对于在Java层实现的Android应用程序窗口来说,它也需要请求SurfaceFlinger服务为它创建绘图表面,这个绘图表面使用一个Surface对象来描述。由于在Java层实现的Android应用程序窗口还要接受WindowManagerService服务管理,因此,它的绘图表面的创建流程就会比在C++层实现的应用程序窗口复杂一些。具体来说,就是在在Java层实现的Android应用程序窗口的绘图表面是通过两个Surface对象来描述,一个是在应用程序进程这一侧创建的,另一个是在WindowManagerService服务这一侧创建的,它们对应于SurfaceFlinger服务这一侧的同一个Layer对象,如图1所示:

图1 应用程序窗口的绘图表面的模型图
在应用程序进程这一侧,每一个应用程序窗口,即每一个Activity组件,都有一个关联的Surface对象,这个Surface对象是保在在一个关联的ViewRoot对象的成员变量mSurface中的,如图2所示:

图2 应用程序窗口在应用程序进程这一侧的Surface的实现
图2的类关系图的详细描述可以参考前面Android应用程序窗口(Activity)实现框架简要介绍和学习计划一文的图6,这里我们只关注Surface类的实现。在应用程序进程这一侧,每一个Java层的Surface对都对应有一个C++层的Surface对象,并且后者的地址值保存在前者的成员变量mNativeSurface中。C++层的Surface类的实现以及作用可以参考前面Android应用程序与SurfaceFlinger服务的关系概述和学习计划这个系列的文章。
在WindowManagerService服务这一侧,每一个应用程序窗口,即每一个Activity组件,都有一个对应的WindowState对象,这个WindowState对象的成员变量mSurface同样是指向了一个Surface对象,如图3所示:

图3 应用程序窗口在WindowManagerService服务这一侧的Surface的实现
图3的类关系图的详细描述可以参考前面Android应用程序窗口(Activity)实现框架简要介绍和学习计划一文的图7,这里我们同样只关注Surface类的实现。在WindowManagerService服务这一侧,每一个Java层的Surface对都对应有一个C++层的SurfaceControl对象,并且后者的地址值保存在前者的成员变量mSurfaceControl中。C++层的SurfaceControl类的实现以及作用同样可以参考前面Android应用程序与SurfaceFlinger服务的关系概述和学习计划这个系列的文章。
一个应用程序窗口分别位于应用程序进程和WindowManagerService服务中的两个Surface对象有什么区别呢?虽然它们都是用来操作位于SurfaceFlinger服务中的同一个Layer对象的,不过,它们的操作方式却不一样。具体来说,就是位于应用程序进程这一侧的Surface对象负责绘制应用程序窗口的UI,即往应用程序窗口的图形缓冲区填充UI数据,而位于WindowManagerService服务这一侧的Surface对象负责设置应用程序窗口的属性,例如位置、大小等属性。这两种不同的操作方式分别是通过C++层的Surface对象和SurfaceControl对象来完成的,因此,位于应用程序进程和WindowManagerService服务中的两个Surface对象的用法是有区别的。之所以会有这样的区别,是因为绘制应用程序窗口是独立的,由应用程序进程来完即可,而设置应用程序窗口的属性却需要全局考虑,即需要由WindowManagerService服务来统筹安排,例如,一个应用程序窗口的Z轴坐标大小要考虑它到的窗口类型以及它与系统中的其它窗口的关系。
说到这里,另外一个问题又来了,由于一个应用程序窗口对应有两个Surface对象,那么它们是如何创建出来的呢?简单地说,就是按照以下步骤来创建:
1. 应用程序进程请求WindowManagerService服务为一个应用程序窗口创建一个Surface对象;
2. WindowManagerService服务请求SurfaceFlinger服务创建一个Layer对象,并且获得一个ISurface接口;
3. WindowManagerService服务将获得的ISurface接口保存在其内部的一个Surface对象中,并且将该ISurface接口返回给应用程序进程;
4. 应用程序进程得到WindowManagerService服务返回的ISurface接口之后,再将其封装成其内部的另外一个Surface对象中。
那么应用程序窗口的绘图表面又是什么时候创建的呢?一般是在不存在的时候就创建,因为应用程序窗口在运行的过程中,它的绘图表面会根据需要来销毁以及重新创建的,例如,应用程序窗口在第一次显示的时候,就会请求WindowManagerService服务为其创建绘制表面。从前面Android应用程序窗口(Activity)的视图对象(View)的创建过程分析一文可以知道,当一个应用程序窗口被激活并且它的视图对象创建完成之后,应用程序进程就会调用与其所关联的一个ViewRoot对象的成员函数requestLayout来请求对其UI进行布局以及显示。由于这时候应用程序窗口的绘图表面尚未创建,因此,ViewRoot类的成员函数requestLayout就会请求WindowManagerService服务来创建绘图表面。接下来,我们就从ViewRoot类的成员函数requestLayout开始,分析应用程序窗口的绘图表面的创建过程,如图4所示:

图4 应用程序窗口的绘图表面的创建过程
这个过程可以分为10个步骤,接下来我们就详细分析每一个步骤。
Step 1. ViewRoot.requestLayout
static void setSurface(JNIEnv* env, jobject clazz, const sp<Surface>& surface){ Surface* const p = (Surface*)env->GetIntField(clazz, so.surface); if (surface.get()) { surface->incStrong(clazz); } if (p) { p->decStrong(clazz); } env->SetIntField(clazz, so.surface, (int)surface.get());} 这个函数定义在文件frameworks/base/core/jni/android_view_Surface.cpp中。全局变量so是一个类型为so_t的结构体,在前面的Step 9中,我们已经分析过它的定义了,函数setSurface首先通过它的成员变量surface来将参数clazz所描述的一个Java层的Surface对象的成员变量mNativeSurface转换成一个Surface对象p。如果这个Surface对象p存在,那么就需要调用它的成员函数decStrong来减少它的强引用计数,因为接下来参数clazz所描述的一个Java层的Surface对象不再通过成员变量mNativeSurface来引用它了。
函数setSurface接下来就会将参数surface所指向的一个C++层的Surface对象的地址值保存在参数clazz所描述的一个Java层的Surface对象的成员变量mNativeSurface中。在执行这个操作之前,函数setSurface需要调用参数surface所指向的一个C++层的Surface对象的成员函数incStrong来增加它的强引用计数,这是因为接下来它要被参数clazz所描述的一个Java层的Surface对象通过成员变量mNativeSurface来引用了。
至此,我们就分析完成Android应用程序窗口的绘图表面的创建过程了。通过这个过程我们就可以知道:
1. 每一个应用程序窗口都对应有两个Java层的Surface对象,其中一个是在WindowManagerService服务这一侧创建的,而另外一个是在应用程序进程这一侧创建的。
2. 在WindowManagerService服务这一侧创建的Java层的Surface对象在C++层关联有一个SurfaceControl对象,用来设置应用窗口的属性,例如,大小和位置等。
3. 在应用程序进程这一侧创建的ava层的Surface对象在C++层关联有一个Surface对象,用来绘制应用程序窗品的UI。
理解上述三个结论对理解Android应用程序窗口的实现框架以及WindowManagerService服务的实现都非常重要。 一个应用程序窗口的绘图表面在创建完成之后,接下来应用程序进程就可以在上面绘制它的UI了。在接下来的一篇文章中,我们就继续分析Android应用程序窗品的绘制过程,敬请关注!
- 2楼new_abc42分钟前
- 老罗,请问一下你上面的三个类型的图都 是用什么工具画出来的呀
- Re: Luoshengyang32分钟前
- 回复new_abcn第一个是用word画的,其余的都是用magicdraw uml画的。
- 1楼a876367641小时前
- 果然。。一周一更。每次更新时间都是周一凌晨00:55~01:00.。n辛苦。nn话说,“应用程序进程申请到的图形缓冲区填充好UI数据后,就是用来创建窗口纹理的。这些纹理最终会被合成到SurfaceFlinger服务所使用的一个主图形缓冲区中,然后再渲染到FB里面去。”。。。这里面的SF使用的一个主图形缓冲区?SF自身使用的就是FB,那此处的主图形缓冲区我的理解就是 双Buffer中的backBuffer,是吧?
- Re: Luoshengyang1小时前
- 回复a87636764n这里说“主图形缓冲区”,主要是用来区分SF和应用程序进程所使用的图形缓冲区。SF使用的图形缓冲区是直接在FB上分配的,而应用程序进程使用的图形缓冲区是在匿名共享内存中分配的。再者,应用程序进程使用的图形缓冲区最终都是要合成到SF使用的图形缓冲区去的,因此,就将SF使用的图形缓冲区称为“主图形缓冲区”。对双缓冲区技术,可以在硬件和软件上进行支持。例如,应用程序进程也是使用双缓冲技术来渲染窗口的,即它拥有的其实是一个图形缓冲区队列,而不是一个单独的图形缓冲区,而SF也是拥有一个图形缓冲区队列,队列的大小取决于FB可以分配的图形缓冲区的数量。
