Android 图形系统全景

Android 图形系统横跨应用、系统服务和硬件,组件多、术语杂。本文从应用到硬件梳理一遍:六层分层、层间如何协作、典型场景如何接入,只讲整体,不深入实现细节。

1. 图形系统解决什么问题

屏幕上同时跑着应用界面、视频和系统状态栏,来源不同、节奏各异,最后要合成一帧输出。图形系统要同时处理这几件事:

  • 为 Canvas、OpenGL ES、Vulkan、Camera、视频解码器等不同内容源提供统一输出方式。
  • 在应用、系统服务和硬件之间共享大块图形内存。
  • 同时管理多个独立图像流。
  • 将多个窗口、视频和系统 UI 合成为最终画面。
  • 输出到物理屏幕、虚拟显示或其他图像消费者。
  • 平衡吞吐、延迟、内存、功耗、颜色正确性和内容安全。

2. 从应用到硬件的六层分层

用分层视角建立整体认识:每层解决一类问题,层间通过明确接口衔接。应用开发者最常接触的 View.drawCanvas 位于分层的中上段,是绘制意图的入口;最底层的 GPU、HWC、Display 决定像素如何真正上屏。中间各层把意图一步步转成可上屏的图形数据。

plain 复制代码
应用侧
  1 应用 UI 层 --- View · ViewGroup · Drawable · Window
        │  View.draw
        ▼
  2 绘制接口层 --- Canvas · DisplayList/RenderNode · Picture
        │  RenderThread 回放
        ▼
  3 渲染引擎层 --- Skia · OpenGL ES · Vulkan
        │  写入 Surface
        ▼
系统侧
  4 缓冲通道层 --- Surface · BufferQueue · GraphicBuffer · Fence
        │  提交 Buffer
        ▼
  5 合成协调层 --- WindowManager · SurfaceControl · SurfaceFlinger · Layer
        │  交付合成
        ▼
硬件侧
  6 硬件显示层 --- Gralloc · GPU · HWC · Display

第 1 层 应用 UI 层

描述界面结构与内容,回答"画什么、怎么布局"。

  • View/ViewGroup:界面由 View 树构成,系统对 View 树做 measure(测量)、layout(定位)、draw(绘制)三遍遍历。
  • Drawable:可复用的图形资源(位图、矢量、状态选择器等),被 View 持有并绘制。
  • Window:应用侧的窗口抽象,一个 Window 通常对应一块可显示区域。

这层不关心像素如何生成,只产出界面要长什么样的描述。

第 2 层 绘制接口层

把 View 的绘制意图表达成一串可回放的绘制操作,与具体渲染后端解耦。硬件加速下,onDraw 里写的 canvas.drawText 不会立刻画到屏幕,而是先记成指令,交给渲染线程稍后执行。

  • Canvas:绘制指令接口,提供画几何、文字、图片等操作。两种工作模式:软件模式下直接画进 Bitmap 内存;硬件模式下把指令记录成 DisplayList。
  • DisplayList/RenderNode :硬件加速时,View.draw 经 Canvas 把指令记录成 DisplayList(现代实现称 RenderNode),交给 RenderThread 异步回放,避免阻塞主线程。
  • Picture:可记录并整体回放的一组绘制指令。

Canvas 的位置有点特殊:既是应用调用的接口,又是渲染引擎的输入。这里把它放在"指令的抽象与记录"这层,执行交给渲染引擎。

第 3 层 渲染引擎层

把绘制指令或图形 API 调用转成像素。前两层只描述画什么,到这层把指令变成像素。

  • Skia:跨平台 2D 图形引擎,提供文字、图片、路径等绘制能力,是 Android 硬件加速 2D 渲染(HWUI)使用的渲染引擎。Skia 自身可选不同渲染后端:软件光栅化(CPU),或经 OpenGL ES/Vulkan 调用 GPU。
  • OpenGL ES / Vulkan:面向 GPU 的底层图形 API,用于向 GPU 提交绘制任务。游戏和需要高性能图形的应用直接使用它们。
  • RenderThread:硬件加速下的渲染线程,回放 DisplayList,调用 Skia→GLES/Vulkan→GPU 生成像素。

这层产出的像素最终写入第 4 层的 Surface。

第 4 层 缓冲通道层

负责图形数据在组件间的传输与同步。

  • Surface:生产者提交图像的统一入口;Canvas、GLES、Vulkan、Camera、MediaCodec 都可把结果提交到 Surface。
  • ANativeWindow:Surface 背后的原生抽象,是 C/C++ 层接入 BufferQueue 的方式;EGLSurface 则是 GLES 接入 Surface 的封装。
  • BufferQueue:生产者/消费者之间的缓冲池与队列,协调 Buffer 的分配、入队、出队。
  • GraphicBuffer/HardwareBuffer:图形内存的框架表示,承载一帧或一层图像数据。
  • Fence:表达异步硬件任务(如 GPU 渲染、显示读取)何时完成,避免在数据未就绪时读取。

数据流模型:生产者从 BufferQueue 申请 Buffer → 写入内容 → 提交回队列 → 消费者取走使用 → 用完归还。^1^

第 5 层 合成协调层

状态栏、应用窗口、导航栏各自独立绘制,最后要叠成一幅画面。这层管理窗口与图层,把多个图层合成成最终画面。

  • WindowManager:系统服务,管理窗口的创建、属性与层级关系。
  • SurfaceControl:操作 Layer 的句柄,用于配置图层的位置、尺寸、层级、变换和可见性。
  • SurfaceFlinger:系统合成服务,收集所有可见 Layer 的 Buffer,按层级与属性合成。
  • Layer:系统合成时处理的图层单位,一个 Surface 对应一个 Layer。

这层的特征是控制流与数据流分离:WindowManager/SurfaceControl 负责配置图层,SurfaceFlinger 负责合成 Buffer。

第 6 层 硬件显示层

提供图形内存、算力与物理输出。前五层的逻辑最终都落到这层的硬件上执行。

  • Gralloc:图形内存分配器,为 GPU、显示控制器、Camera 等不同硬件分配可共享的图形内存,是零拷贝传递的关键。
  • GPU:执行渲染引擎提交的绘制任务,也可参与合成。
  • HWC(Hardware Composer):显示控制器硬件,能高效合成多个 Layer。SurfaceFlinger 把 Layer 交给 HWC,HWC 决定哪些 Layer 由硬件合成、哪些超出能力需回退 GPU 合成。
  • Display:物理屏幕或虚拟显示,是最终输出目标。

3. 层级之间的关系

分层建立了纵向认识,这一节用两条主线把它们串起来:数据流(图像怎么从指令变成屏幕像素)和控制流(窗口和图层怎么被配置)。两条线在不同层交汇,由 Producer/Consumer 模型贯穿。

数据流:从绘制指令到屏幕像素

plain 复制代码
View 树遍历
   │ View.draw
   ▼
Canvas 记录指令 ──► DisplayList/RenderNode
                          │ RenderThread 回放
                          ▼
Skia / GLES / Vulkan ──► 生成像素
   │ 写入
   ▼
Surface ──► BufferQueue ──► SurfaceFlinger 取 Buffer
                                  │ 合成
                                  ▼
                           HWC / GPU 合成
                                  │
                                  ▼
                              Display 上屏

普通应用窗口沿这条路径完整走一遍:应用侧(1-3 层)生成像素写入 Surface,系统侧(4-6 层)把多个 Surface 的 Buffer 合成上屏。Camera、视频解码等场景作为生产者,可直接从第 3、4 层接入,跳过 1、2 层的 View 体系。^2^

控制流:窗口与图层的配置

数据流之外,还有一条并行的控制流:

plain 复制代码
WindowManager / SurfaceControl
   │ 配置 Layer 属性(位置、层级、变换、可见性)
   ▼
SurfaceFlinger ──► 按配置合成可见 Layer ──► HWC/Display

Window 是第 1 层应用侧的窗口抽象,它的实际管理发生在第 5 层:WindowManager 通过 SurfaceControl 创建并配置 Layer,SurfaceFlinger 依据这些配置决定合成顺序与效果。界面变化时,控制流(窗口/图层属性更新)与数据流(重新绘制)协同工作。

贯穿全程的 Producer/Consumer 模型

第 3-5 层由一个统一模型贯穿:

  • 生产者:渲染引擎(Skia/GLES/Vulkan)、Camera、MediaCodec 等,产生图形 Buffer。
  • 消费者:SurfaceFlinger(合成上屏)、SurfaceTexture(转纹理继续处理)、ImageReader(图像分析)、MediaCodec Encoder(编码)等。
  • 中介:BufferQueue 连接两端,Buffer 在其中流转。

同一个生产者可对接不同消费者:Camera 的 Buffer 既能给 SurfaceFlinger 上屏预览,也能给 ImageReader 做分析,还能给编码器录像,区别只在于 BufferQueue 连接哪两端。

分层带来的解耦

每层都解决一类解耦问题:

  • 应用与硬件解耦:应用只画到 Surface,无需感知 GPU 型号或屏幕硬件。
  • 指令与执行解耦:DisplayList 把绘制意图与渲染时机分离,主线程记录、RenderThread 异步执行。
  • 生产与消费解耦:BufferQueue 让生产者和消费者速度不同也能协同,多重缓冲提升吞吐。
  • 内存共享解耦:Gralloc 分配的 Buffer 在 GPU、HWC、CPU 间零拷贝传递,Fence 保证异步读写安全。
  • 后端可替换:Skia 可在软件、GLES、Vulkan 后端间切换,上层接口不变。

4. 典型图形场景

用分层视角看不同场景,区别在于从哪一层接入、消费者是谁:

场景 生产者 消费者/输出
普通应用窗口 HWUI/Skia SurfaceFlinger
Canvas 直接绘制 CPU/Skia Canvas SurfaceFlinger
OpenGL ES 游戏 GLES/EGL SurfaceFlinger
Vulkan 游戏 Vulkan Swapchain SurfaceFlinger
Camera 预览 Camera HAL SurfaceFlinger 或 SurfaceTexture
视频播放 MediaCodec Decoder SurfaceFlinger 或 SurfaceTexture
视频编码 Camera/GLES MediaCodec Encoder
图像分析 Camera/Codec ImageReader
屏幕录制 SurfaceFlinger/虚拟显示 MediaCodec Encoder

每个场景都要弄清:生产者是谁、写到哪个 Surface、BufferQueue 连接哪两端、谁最终消费、是否合成上屏。SurfaceView、SurfaceTexture、TextureView 是不同场景的接入方案,而非单独的架构层。^2^

5. 常见疑问

多重缓冲的作用 单缓冲下,生产者画完一帧必须等显示读完才能画下一帧,期间 GPU/CPU 空闲。多缓冲让生产者画到后台 buffer 的同时、显示读取前台 buffer,形成流水线,提升吞吐、降低卡顿。三缓冲更进一步:即使一帧没渲染完,生产者仍总有 buffer 可写。

BufferQueue 如何应对生产与消费速度不匹配 队列在两端之间蓄水:生产快时 buffer 堆积到上限会阻塞生产者或丢帧;生产慢时消费者复用上一帧。多重缓冲让速度不匹配的两端按各自节奏运转,互不阻塞。

为什么避免直接复制整帧像素 一帧数据量很大(1080p RGBA 约 8MB),复制既耗时又耗带宽和功耗。Android 用 Gralloc 分配可被 GPU、CPU、HWC 共享访问的图形内存,组件间传递的是内存句柄而非数据本身,零拷贝完成流转。

HWC 与 GPU 在性能和功耗上的取舍 HWC 是显示控制器硬件,合成多 layer 比用 GPU 省电:专用硬件、不占 GPU,还能让 GPU 休眠。SurfaceFlinger 优先用 HWC 合成,只有超出 HWC 能力(复杂变换、layer 过多、特殊混合模式)才回退 GPU,此时既耗电又占用应用渲染资源。

Fence 的作用 图形任务(GPU 渲染、显示读取、Camera 输出)是异步硬件操作,CPU 提交后无法同步等待完成。Fence 是内核同步原语,标记"这块 buffer 何时写完或读完"。没有它,消费者可能读到半成品,或生产者覆盖还在被读的 buffer。它用信号替代忙等,保证异步硬件链路的正确顺序。

SurfaceView 与 TextureView 的行为差异 SurfaceView 拥有独立 Surface,直接作为独立 layer 交给 SurfaceFlinger 合成,不参与 View 体系。性能好,但变换和透明受限,还可能和 View 层叠时出现缝隙。 TextureView 把内容当作纹理,先写入 Buffer,再作为纹理合成进 View 的绘制结果,完全纳入 View 层级,支持任意变换与透明,代价是多一次纹理合成。 两者本质区别:独立 layer 合成,还是作为纹理纳入 View 层级。

部分视频无法截图的原因 受 DRM 保护的内容(如 Widevine L1)在安全硬件中解码,输出到受保护的 buffer,标记为 protected,不允许 GPU 或 CPU 读取。截图要读 framebuffer,SurfaceFlinger 对这类 layer 标记 secure,截图时跳过,这是内容安全机制。

相同分辨率下 Buffer 占用的差异来源 取决于像素格式和分配方式。同分辨率下,RGBA_8888(每像素 4 字节)是 RGB_565(每像素 2 字节)的两倍;是否启用帧缓冲压缩(如 AFBC)、不同 usage 标志导致的对齐与布局差异,都会改变实际占用。

物理显示与虚拟显示的区别 物理 Display 接真实屏幕硬件,经 HWC 输出,直接给用户看。 虚拟 Display 没有真实硬件,把合成结果输出到一个可被应用消费的 Surface,用于屏幕录制、投屏等。合成结果不直接上屏,而是作为 buffer 送出。

WindowManager 与 SurfaceFlinger 的分工 WindowManager 管窗口的语义和策略(创建、属性、层级、焦点),SurfaceFlinger 管像素层面的合成(收集 buffer、合成、输出)。分开后 WM 不需懂合成,SF 不需懂窗口策略;SF 跑在独立进程,合成实时性强,不能被窗口管理逻辑阻塞。两者通过 SurfaceControl 桥接:WM 配置 layer,SF 执行合成。

小结

从 View.draw 到屏幕像素,要穿过应用 UI、绘制接口、渲染引擎、缓冲通道、合成协调、硬件显示六层,由数据流和控制流两条主线贯穿。带着这个框架去看 BufferQueue、SurfaceFlinger 或 HWC 的专题,每块都能对应到具体的层和主线。

Footnotes

  1. Android Graphics 概览

  2. 图形架构 2

相关推荐
qq_425516181 小时前
录音转文字工具免费下载:免费额度与功能限制对比
android·人工智能·智能手机·powerpoint
plainGeekDev1 小时前
Robolectric → 分层测试:测试策略重构
android·java·kotlin
plainGeekDev1 小时前
Instrumentation → Compose Testing
android·java·kotlin
sz_denny2 小时前
android aab导出apk
android
码上有光2 小时前
make和makefile(自动化构建)
android·运维·自动化
艾莉丝努力练剑2 小时前
【MYSQL】MYSQL学习的一大重点:视图
android·服务器·数据库·学习·mysql·面试·视图
艾莉丝努力练剑2 小时前
【MYSQL】MYSQL学习的一大重点:事务(下)- InnoDB 事务隔离性原理(MVCC 视角)
android·数据库·b树·sql·学习·mysql·面试
三少爷的鞋3 小时前
launch 不是用来切线程的:为什么我越来越少在它后面写 Dispatchers.IO
android
爱和冰阔落3 小时前
【Linux】从匿名管道到进程池:任务派发、fd 继承 Bug 与完整实现
android·linux·运维·c++