共计 2845 个字符,预计需要花费 8 分钟才能阅读完成。
图形系统是 Android 中非常重要的子系统,与其他子系统相互协作,完成图形界面的渲染和显示。
概述
官方提供了一个图形系统的关键组件协作图,如下所示:
这幅图大致描述了图形数据的流转:OpenGL ES、MediaPlayer 等生产者生产图形数据到 Surface,Surface 通过 IGraphicBufferProducer
把GraphicBuffer
跨进程传输给消费者 SurfaceFlinger
,SurfaceFlinger
根据 WMS
提供的窗口信息合成所有的 Layer
(对应于 Surface),具体的合成策略由hwcomposer
HAL 模块决定并实施,最后也是由该模块送显到 Display,而Gralloc
模块则负责分配图形缓冲区。不过该图缺乏层次感,通过下图我们详细分析整个流程。
大体上,应用开发者可以通过两种方式将图像绘制到屏幕上:
- Canvas
- OpenGL ES
Canvas
是一个 2D 图形 API,是 Android View 树实际的渲染者。Canvas
又可分为 Skia
软件绘制和 hwui
硬件加速绘制。
Android4.0 之前默认是 Skia
绘制,该方式完全通过 CPU 完成绘图指令,并且全部在主线程操作,在复杂场景下单帧容易超过 16ms 导致卡顿。
从 Android4.0 开始,默认开启硬件加速渲染,而且 5.0 开始把渲染操作拆分到了两个线程:主线程和渲染线程,主线程负责记录渲染指令,渲染线程负责通过 OpenGL ES
完成渲染,两个线程可以并发执行。
除了 Canvas
,开发者还可以在异步线程直接通过OpenGL ES
进行渲染,一般适用于游戏、视频播放等独立场景。
从应用侧来看,不管是 Canvas
,还是OpenGL ES
,最终渲染到的目标都是 Surface,现在比较流行的跨平台 UI 框架Flutter
在 Android 平台上也是直接渲染到 Surface。Surface 是一个窗口,例如:一个 Activity 是一个 Surface、一个 Dialog 也是一个 Surface,承载了上层的图形数据,与 SurfaceFlinger 侧的 Layer 相对应。
Native 层 Surface 实现了 ANativeWindow
结构体,在构造函数中持有一个 IGraphicBufferProducer
,用于和BufferQueue
进行交互。BufferQueue
是连接 Surface 和 Layer 的纽带,当上层图形数据渲染到 Surface 时,实际是渲染到了 BufferQueue
中的一个 GraphicBuffer
,然后通过IGraphicBufferProducer
把GraphicBuffer
提交到BufferQueue
,让 SurfaceFlinger 进行后续的合成显示工作。
SurfaceFlinger 负责合成所有的 Layer 并送显到 Display,这些 Layer 主要有两种合成方式:
OpenGL ES
:把这些图层合成到 FrameBuffer,然后把 FrameBuffer 提交给hwcomposer
完成剩余合成和显示工作。hwcomposer
:通过HWC
模块合成部分图层和 FrameBuffer,并显示到 Display。
BufferQueue
Android 图形系统包含了两对生产者和消费者模型,它们都通过 BufferQueue 进行连接:
Canvas
和OpenGL ES
生产图形数据,SurfaceFlinger
消费图形数据。SurfaceFlinger
合成所有图层的图形数据,Display 显示合成结果。
Surface 属于 APP 进程,Layer 属于系统进程,如果它们之间只用一个 Buffer,那么必然存在显示和性能问题,所以图形系统引入了 BufferQueue
,一个 Buffer 用于绘制,一个 Buffer 用于显示,双方处理完之后,交换一下 Buffer,这样效率就高很多了。BufferQueue
的通信流程如下所示:
- 生产者从 BufferQueue 出队一个空闲 GraphicBuffer,交给上层填充图形数据;
- 数据填充后,生产者把装载图形数据的 GraphicBuffer 入队到 BufferQueue,也可以丢弃这块 Buffer,直接 cancelBuffer 送回到 BufferQueue;
- 消费者通过
acquireBuffer
获取一个有效缓存; - 完成内容消费后(比如上屏),消费者调用
releaseBuffer
把 Buffer 交还给 BufferQueue。 GraphicBuffer
代表的图形缓冲区是由Gralloc
模块分配的,并且可以跨进程传输(实际传输的只是一个指针)。- 通常而言,APP 端使用的是 BufferQueue 的
IGraphicBufferProducer
接口(在 Surface 类里面),用于生产;SurfaceFlinger 端使用的是 BufferQueue 的IGraphicBufferConsumer
接口(在 GLConsumer 类里面),用于消费。
Surface 与 SurfaceFlinger
Surface
表示 APP 进程的一个窗口,承载了窗口的图形数据,SurfaceFlinger
是系统进程合成所有窗口(Layer)的系统服务,负责合成所有 Surface 提供的图形数据,然后送显到屏幕。SurfaceFlinger
既是上层应用的消费者,又是 Display 的生产者,起到了承上启下的作用。官方提供了一个架构图,如下所示:
该图可能较抽象,我们通过一个实例理解下这层关系,下图是微信添加朋友的弹窗界面:
<img src=”https://ltlovezh.oss-cn-beiji…; width=”300″ />
我们可以通过 adb shell dumpsys SurfaceFlinger
查看该界面包含几个窗口(Surface):
从 SurfaceFlinger
的 dump 信息可以看到:
com.tencent.mm/com.tencent.mm.ui.LauncherUI#0
是微信的主窗口,并且铺满了整个屏幕(0,0,1080,2340)
。PopupWindow:7020633#0
是弹起的PopupWindow
,它是一个独立的窗口(Surface),屏幕坐标范围是(599,210,1058,983)
。StatusBar#0
表示系统状态栏,由系统进程负责绘制,屏幕坐标范围是(0,0,1080,80)
,即此状态栏高 80 像素。NavigationBar#0
表示系统导航栏,由系统进程负责绘制,屏幕坐标范围是(0,2214,1080,2340)
,即此导航栏高 126 像素。- 最后两个窗口也是系统窗口,具体作用不知。
- 上述所有图层的合成类型都是
Device
,即HWC
硬件模块负责合成这些 Layer。 SurfaceFlinger
会合成上述所有图层(Layer),并送显到内嵌的Display 0
。
总结
本篇文章从上到下简述了 Android 图形系统的流转流程,以及承载图形数据流转的重要结构:BufferQueue
,最后通过 dump 信息论证了多 Surface 实例。