基础
1.硬件基石:渲染与合成分离
Android图形显示依赖两大核心硬件单元:
GPU(图形处理器):负责渲染。将应用下发的绘图指令(如画圆、画线)转化为具体的像素数据(二维数组),产出单个图层。
DPU(显示处理器):负责合成与送显。将多个渲染好的图层(如状态栏、壁纸、应用界面)按Z轴顺序合并,计算重叠区域的最终颜色,并驱动屏幕显示。
HWC(硬件合成器):HAL层的抽象接口,厂商实现DPU驱动。面试核心点:HWC有图层合成数量上限,超出部分会回退给GPU处理(标记为CLIENT合成),GPU处理不完则丢帧。
2.核心数据流转:GraphicBuffer与BufferQueue
GraphicBuffer:图形数据的载体,本质是一块共享内存(分配在RAM,移动端无独立显存)。
Fence机制:跨硬件同步锁。GPU/DPU/CPU访问GraphicBuffer前需检查Fence信号,防止数据读写冲突(如GPU还在写,DPU就来读,导致花屏)。
BufferQueue:基于生产者-消费者模式。APP是生产者(Surface作为图像的生产者,持有BufferQueue的引用,并且封装了出列和入列两个方法,dequeue获取空缓冲,queue提交绘满的缓冲);SF(SurfaceFlinger)是消费者(acquire取出渲染完的缓冲,release释放回空闲队列)。状态流转:FREE → DEQUEUED → QUEUED → ACQUIRED → FREE。
3.双端进程职责
APP进程(生产者):通过ViewRootImpl持有Surface,触发绘制后提交Buffer给BufferQueue。
SurfaceFlinger进程(消费者):核心系统服务,接收APP的Buffer,执行图层合成,最后通过DRM提交给硬件显示。
4.VSync驱动机制(灵魂)
为了画面流畅,系统引入VSync(垂直同步)信号。为了解决“渲染+合成”耗时长的问题,Offset偏移量设计让APP和SF接收信号的时机错开,理论上可在一个硬件周期内完成两步,降低触控延迟。
正常的显示流程:
VSync1 VSync2 VSync3 │ │ │ APP 开始绘制 │ └── queueBuffer │ SF 获取Buffer 调用hwc合成, 执行合成 │ └── 提交显示 │ Display 扫描显示ViewRootImpl.requestLayout() 的执行流程
在 Activity 的 onResume 周期后,会调用 WindowManager.addView() 方法, 其内部 会 创建 ViewRootImpl, 并且会 通过 Binder 给 WMS 添加 窗口。
ViewRootImpl.requestLayout() ↓ scheduleTraversals() ↓ 1. 向 MessageQueue 插入同步消息屏障 postSyncBarrier() ↓ 2. 向 Choreographer 注册 CALLBACK_TRAVERSAL ↓ 3. Choreographer 请求/等待下一次 VSync ↓ VSync 到来 ↓ DisplayEventReceiver 收到 VSync ↓ 向主线程 MessageQueue 投递一个“异步消息” ↓ 因为队列前面有同步消息屏障: 普通同步消息先被挡住 异步消息可以越过屏障 ↓ 主线程 Looper 取出这个异步消息 ↓ 执行 Choreographer.doFrame() ↓ 执行 CALLBACK_TRAVERSAL ↓ ViewRootImpl.doTraversal() ↓ 先移除同步消息屏障 removeSyncBarrier() ↓ performTraversals() ↓ measure → layout → draw (绘制三部曲)measure过程和layout过程都是发生在CPU,draw不同,如果开启硬件加速,那么draw的过程发生在GPU。
requestLayout() 最终会调用ViewRootImpl.scheduleTraversals(),首先向主线程的 MessageQueue插入一个同步消息屏障,然后通过 Choreographer 注册 Traversal 回调,等待 下一次 VSync 信号。
当 VSync 到来后,DisplayEventReceiver 接收到 VSync,并通过 Choreographer 向主线程消息队列投递一个异步消息。由于同步消息屏障会阻塞普通同步消息,而异步消息可以越过屏障,所以这一帧的绘制任务能够被优先处理。
随后主线程执行 Choreographer.doFrame(),触发 Traversal 回调,进入 ViewRootImpl.doTraversal(),移除同步消息屏障,再调用performTraversals(),最终完成 View 的 measure、layout 和 draw 流程。
后续 从绘制 到 屏幕 显示的过程:
我会把整个流程分为四个阶段:生产者入队 → SF调度触发 → 合成决策与执行 → 硬件呈现。
第一阶段:生产者提交(APP侧)
- APP完成一帧绘制后,调用
Surface.queueBuffer()。 - 此时,GraphicBuffer 被放入 BufferQueue。这一步本质上是释放生产者锁,让消费者(SF)有机会获取。
第二阶段:SF唤醒与VSync对齐(核心调度)
3. BufferQueue通知SF:onFrameAvailable()。
4. SF调用signalLayerUpdate()→requestNextVSync()。注意,SF不会立即合成,而是等待下一个VSync信号。这么做是为了对齐刷新率,避免在屏幕刷新中途写入数据导致撕裂。
第三阶段:VSync到来与消息驱动(双消息机制)
5. VSync信号触发 DisplayEventReceiver,SF开始处理。
6. SF首先发INVALIDATE消息:在handleMessageInvalidate()中处理事务(Transaction)和图层更新。这一步是为了判断是否有图层需要重绘或合成,如果没有变化,则跳过合成以省电。
7. 若需要合成,则发REFRESH消息,进入真正的合成流程。这种将“检查”和“执行”分离的设计,是为了高效处理高频UI变化。
第四阶段:合成与送显(SF核心工作)
8. 在handleMessageRefresh()中,SF执行五步标准合成流水线:
- preComposition():最后检查是否有新Buffer,防丢帧。
- rebuildLayerStacks():计算可见区域和脏区,只合变化的部分。
- setUpHWComposer():决策分水岭。决定哪些层走HWC(硬件合成器),哪些层走GPU(Client合成)。原则是:能走HWC就不走GPU,因为HWC是硬解码专用电路,功耗更低且零拷贝。
- doComposition():执行合成。GPU合成会渲染到Framebuffer;HWC合成则只是配置显示控制器的寄存器。
- postComposition():收尾,触发BufferQueue的release回调,让APP复用Buffer。
- 最终,Display Controller (显示控制器)基于VSync周期扫描Framebuffer,将图像发送给屏幕,用户看到画面。
APP 绘制完成后,通过 Surface.queueBuffer() 将 GraphicBuffer 提交到 BufferQueue。BufferQueue 可以理解为 APP 和 SurfaceFlinger 之间传递图形 Buffer 的缓冲队列。SF 侧发现有新的 Buffer 可以消费后,会安排下一次 VSync;VSync 到来时,SF 先检查图层和事务是否发生变化,如果需要合成,就通过 GPU/HWC 完成多个 Layer 的合成,并将最终显示内容提交给显示系统。显示控制器会按照 VSync 对应的显示时序切换到新的显示 Buffer,并逐行扫描输出到屏幕,最终用户看到这一帧。
GraphicBuffer = 这个图层某一帧的像素数据
BufferQueue = 这个图层的 Buffer 流转队列