在Android App中,我们经常会看到页面与页面之间的切换,页面弹出输入法以及页面弹出窗口等,这种情况实际就是一个Activity中对应了一个窗口,页面之间的切换既是Activity生命周期的调用,也是两个窗口之间哪个展示在前,同理的,输入法也是一个窗口,在页面弹出的也是一个子窗口,由此得出,在Android系统中,我们需要一个管理类用来管理窗口的,而这就是我们经常看到的WindowManagerService(WMS)服务发挥的作用。各个Activity对应的Window的状态实际由WindowState来管理。我们在启动Activity的时候,实际上就涉及应用程序侧——WMS——AMS三者之间的关联,如下图所示:





Android12 settings原生设置图标_UI


我们在Activity启动一节提到,Activity组件的启动由AMS来管理,和Activity组件启动相关的对象有包括ActivityRecord,用来记录Activity运行状态。我们在开发过程中,经常出现在弹出弹窗时,提示相关弹窗token不存在的情况,这个bug暂且不追究什么原因引起,我们只要知道,窗口是需要对应一个token的,在Activity中,对应有个AppWindowToken,其在Activity启动的过程中由WMS创建,既然Activity有多个,而AppWindowToken是由WMS创建的,可想而知,AppWindowToken一定是记录在一个Collections中,如Hashmap,list等。AppWindowToken的作用实际就是一个标识符,且和Activity是唯一对应的。为啥需要AppWindowToken,实际上因为有WMS和AMS相互参与,因此使用唯一的token来标记对应的Activity就显得有必要。

IWindow和IWindowSession都为Binder本地对象,用于应用程序和WMS之间的相关调用。通过IWindowSession通过WMS为Activity创建一个WindowState对象(WindowState的作用下面会说到),通过调用IWindowSession的relayout来请求Activity进行UI布局,使得Activity可以在屏幕上显示。IWindow具体对象引用在WMS创建的WindowState的mClient属性中,通过这个属性,当Activity窗口大小、可见性、焦点等变化的时候,通过IWindow的相关方法通知到Activity,从而进行对应的调整。

此外,WMS在为Activity创建一个WindowState过程中,通过调用attach方法创建了Java层的SufaceSession对象,Java层的SurfaceSession对象对应用C++层的SurfaceComposerClient对象,因此Java层的应用程序就可以通过SurfaceSession来和SurfaceFlinger建立了联系。

那么窗口有了,正如一扇窗的框架有了,但是我们需要安装的是玻璃,否则,窗只是个框架,没有任何实质的东西,因此,形如Activity的内容正是一扇窗的玻璃。那么这个窗玻璃究竟有哪些知识点呢?

我们知道,Activity启动的流程中,也是创建UI界面的过程,既窗口的绘图表面。窗口的绘图表面实际涉及到几个数据结构。

ViewRoot(ViewRootImpl)——通过它可以对UI进行刷新布局、重新渲染等;

WindowManagerImpl(WindowManagerGloable)——本地窗口的管理器,本地成员包括三个重要的数组,mViews、mRoots、mParams。用于存储每个窗口对应的UI页面、UI页面对应的指导者viewRoot以及相关的布局;

PhoneWindow——为Activity在启动过程中创建的本地窗口对象;

DecorView——所有应用窗口的父View,包括titleBar和contentView,一般情况下,Activity在onCreate的时候setContentView即设置contentView的UI内容。

这样,我们知道了Activity启动的时候创建了本地窗口phoneWindow, PhoneWindow包含DecorView,该View我们主要的开发点就是contentView,通过contentView开发者可以实现具体需求需要的。本地窗口以及相关的UI View有了,前面说的在Activity启动的时候,创建并设置相关的view过程中会进行WindowState的对象创建。View有了,窗口管理有了,这些可以看成是软件上的,那么怎么变现到显示屏展示呢?我们继续以玻璃窗为例子,有窗为框架了,我们玻璃造型也知道怎么处理了,但是我们需要介子去把这些内容展示出来。而这就是我们常看到的Surface。如下图所示,每个应用程序窗口都对应有两个Java层的Surface对象,其中一个是在WMS服务这一侧创建的,而另一个是在应用程序进程这一侧创建的。在WMS服务这一侧创建的Java层的Surface对象关联C++层的SurfaceControl,用来设置应用窗口的属性、例如大小、位置等,而在应用程序这一侧创建的Java层的Surface对象关联C++层的Surface对象,用来绘制应用程序窗口的UI,既应用程序窗口的绘图表面,而最终,UI图形界面都关联到用到SurfaceFling服务提供的同一个Layer对象。对于SurfaceFling,还是如同上面,将一步步揭开是什么来的。



Android12 settings原生设置图标_应用程序_02


既然我们已经找到了承载体Surface,那么接下来我们来看下应用程序是如何绘制上去的,绘制上去之后,SurfaceFlinger又是怎样提供一个能力让相关的UI展示到显示屏上的。

应用程序的绘制流程在应用层面上来看就是Android的绘制原理,经过onMeasure(窗口的测量)、onLayout(窗口的布局)、onDraw(窗口的绘制)。在onDraw这一步,ViewRoot(ViewRootImpl)首先会创建一个Canvas画布,接着在画布上绘制Android程序创建的的UI, 最后再将已画上内容的画布(Canvas)交给SurfaceFlinger服务来进行渲染。在onDraw这一步,可以区分是硬件加速(使用openGL)还是非硬件加速(使用Skia图形库)来进行栅格化。通过绘制在Canvas画布上一个图形缓存区中,进而SurfaceFlinger通过OpenGL图像块API来将这个图形缓冲区渲染到帧缓冲区中,既绘制到屏幕上。如下图软件绘制和硬件绘制的区别。



Android12 settings原生设置图标_应用程序_03


在Android3.0或者没有启动硬件加速之前,系统启用软件的方式流程如下:



Android12 settings原生设置图标_应用程序_04


从Android3.0开始,就可以支持硬件加速,Android4.0开始,默认开启硬件加速,如下图。



Android12 settings原生设置图标_应用程序_05


我们知道,Android设备的显示屏是用来显示画面的。而应用程序的UI就是渲染在显示屏上的。在内核空间,系统屏幕是一个称为帧缓冲区的硬件设备来描述的。在用户空间,用户空间通过文件描述符形如/dev/graphics/fbn(其中n可为0、1...n)来操作硬件设备。涉及到硬件设备,在HAL究竟是干嘛的一文中说到,硬件抽象层是对硬件设备一种抽象,所谓抽象就是计算机语言结构化,如对应设备的结构体,对应操作的方法等。所以帧缓存区的硬件设备不是真正的硬件设备,实际真正的设备是显卡,通过帧缓存区的映射设备描述(硬件抽象层),可以访问显卡或者显卡的寄存器等。

Android系统在帧缓冲区中提供了一个Gralloc模块,封装了帧缓冲区的所有访问操作。用户空间应用程序在使用帧缓冲区的时候,首先要加载Gralloc模块之后,并且获得一个gralloc设备和fb设备。gralloc设备的作用是应用程序可以通过它申请分配一块图形缓冲区,并且将这块图形缓冲区映射到应用程序的地址空间,从而就可以往里面写入要绘制的内容了。用户空间通过fb设备把前面已经准备的图形缓冲区渲染到帧缓冲区中,既将图形缓冲区绘制到屏幕上。

至此,我们就了解了应用程序的窗口是如何管理了,应用程序的窗口UI是如何绘制的,以及是如何绘制到窗口的绘制表面的,进而在屏幕上显示的。接下来我们围绕SurfaceFlinger服务,展开分析关于缓冲区的问题,以及UI渲染同步机制问题。