本文是 Systrace 系列文章的第五篇,主要是对 SurfaceFlinger 的工作流程进行简单介绍,介绍了 SurfaceFlinger 中几个比较重要的线程,包括 Vsync 信号的解读、应用的 Buffer 展示、卡顿判定等,由于 Vsync 这一块在 Systrace 基础知识 - Vsync 解读 和 Android 基于 Choreographer 的渲染机制详解 这两篇文章里面已经介绍过,这里就不再做详细的讲解了。
本系列的目的是通过 Systrace 这个工具,从另外一个角度来看待 Android 系统整体的运行,同时也从另外一个角度来对 Framework 进行学习。也许你看了很多讲 Framework 的文章,但是总是记不住代码,或者不清楚其运行的流程,也许从 Systrace 这个图形化的角度,你可以理解的更深入一些。
本文目录
系列文章目录
- Systrace 简介
- Systrace 基础知识 - Systrace 预备知识
- Systrace 基础知识 - Why 60 fps ?
- Systrace 基础知识 - SystemServer 解读
- Systrace 基础知识 - SurfaceFlinger 解读
- Systrace 基础知识 - Input 解读
- Systrace 基础知识 - Vsync 解读
- Systrace 基础知识 - Vsync-App :基于 Choreographer 的渲染机制详解
- Systrace 基础知识 - MainThread 和 RenderThread 解读
- Systrace 基础知识 - Binder 和锁竞争解读
- Systrace 基础知识 - Triple Buffer 解读
- Systrace 基础知识 - CPU Info 解读
- Systrace 流畅性实战 1 :了解卡顿原理
- Systrace 流畅性实战 2 :案例分析: MIUI 桌面滑动卡顿分析
- Systrace 流畅性实战 3 :卡顿分析过程中的一些疑问
- Systrace 响应速度实战 1 :了解响应速度原理
- Systrace 响应速度实战 2 :响应速度实战分析-以启动速度为例
- Systrace 响应速度实战 3 :响应速度延伸知识
- Systrace 线程 CPU 运行状态分析技巧 - Runnable 篇
- Systrace 线程 CPU 运行状态分析技巧 - Running 篇
- Systrace 线程 CPU 运行状态分析技巧 - Sleep 和 Uninterruptible Sleep 篇
正文
版本说明(2026-10-14 核查):本文保留 2020 年的 Systrace 图和分析顺序。下方两段完整函数对应 AOSP
android-10.0.0_r1;旧 Binder/BufferQueue 路径、HIDL 轨道名不宜直接套用到新系统。文中另补android-16.0.0_r1的 BLAST、commit/composite 路径,以及 Android 12(API 31)起的 FrameTimeline 分析入口。截图未提供系统构建号,不能仅凭相似函数名认定其精确源码版本。
这篇 SurfaceFlinger 文章需要和 Perfetto 8:Vsync 机制与性能分析 对照阅读。旧 Systrace 里主要看 SurfaceFlinger 主线程、BufferQueue、HWC 和 App Buffer 的对应关系;Perfetto 里仍然看这些对象,但要结合 FrameTimeline、Buffer、Display/HWC 轨道和 CPU 调度。
先定位问题帧及预期呈现时间,再检查 App 提交、GPU 就绪、SurfaceFlinger 锁存(latch)、合成和显示提交。超过 16.6 ms、出现长 Slice,都不能单独证明某一环节导致掉帧。
官方 SurfaceFlinger 文档介绍了基本职责:接收图层内容和状态,组织合成,并把结果交给显示链路。读下面的旧图时注意:
- 状态栏、导航栏、App 是常见例子,不是固定的三个图层。一个 App 可有多个 Surface,窗口和图层也不是简单的一一对应;窗口状态可以通过事务协调更新。
- 屏幕刷新率用 Hz,内容帧率用 fps。60 Hz 只是旧例子,不能把 58~62 或固定 16.6 ms 当作所有设备的范围和预算。App/SF 的调度还涉及预测和相位,不能把每个调度回调都理解成当下的硬件中断。
- 没有新 Buffer 时,图层可以继续显示旧内容;这不同于“从未提交过 Buffer”。纯色等非 Buffer 图层也不适用“没有 Buffer 就忽略”的简单规则。
- App 提交、GPU 完成和显示呈现是不同事件。各帧可以流水重叠;生产速度超过消费速度可能造成背压、额外延迟或丢弃,具体取决于队列模式和调度策略。

这张示意图同时画了 GPU 合成与直接交给显示硬件的路径。图中重复出现的 GPU 不是多个独立 GPU,也不能据此认定每层都经过 SurfaceFlinger 的 GPU 合成。接下来按 App、BufferQueue、SurfaceFlinger、HWComposer 四部分读图;这是依赖顺序,不是所有线程和硬件严格串行的时间线。
App 部分
关于 App 部分,可以先读 MainThread 和 RenderThread 解读。本文截图使用传统 View/HWUI 渲染路径:

App 通过 Surface 等生产者接口提交图像 Buffer;不是把 drawcall 存进图像 Buffer 再交给 SurfaceFlinger 执行。主线程记录绘制操作、RenderThread 提交 GPU 工作、GPU 写入图像内容,是不同阶段。图中第 8 步的“绘制好”应理解为提交了 Buffer 和同步依赖,不保证 GPU 已完成。
沿着图中的三个交互点看:Vsync 回调、dequeueBuffer、queueBuffer。
Vsync 信号的接收和处理
Choreographer 渲染机制解释了帧回调的请求和执行。收到回调后,应用开始处理这一帧的输入、动画和遍历等工作;回调时间戳与线程实际得到 CPU 的时间可能不同。

图中绿色 F 标记是旧查看器的帧分析标记,不能直接当作“这帧已按时上屏”的证据;仍需关联对应 Buffer 与显示帧。
RenderThread 的 dequeueBuffer
dequeueBuffer 为生产者取得可写的 Buffer 槽位及同步信息。旧图中,BufferQueue 的消费端位于 SurfaceFlinger,App 经 Binder 请求;它可能复用已分配的 Buffer,不是每次分配或复制整张图像。
App 端的 Systrace 如下所示
SurfaceFlinger 端的 Systrace 如下所示
图中的 SF Binder 线程处理出队请求;这与 SF 主线程执行合成是两件事。出队或绘制前还可能等待槽位、release fence 等条件,要结合线程状态判断等待原因。
新路径补充:在 Android 16 BLASTBufferQueue 源码中,BLAST 创建本地 BufferQueue,取得排队的 Buffer,再通过 SurfaceControl Transaction 把 Buffer、fence 和图层状态交给 SurfaceFlinger。采用这条路径时,不能再要求每次 dequeueBuffer 都对应一次 App→SF 的 Binder 调用;也不能把所有 BufferQueue 都画在 SF 进程内。
RenderThread 的 queueBuffer
queueBuffer 把生产者持有的 Buffer 提交给消费者,并携带 fence、时间戳等元数据。这里的 eglSwapBuffersWithDamageKHR → queueBuffer 是图中的 OpenGL ES 路径,不是 Vulkan、软件 Canvas 等所有生产方式的共同调用链。
App 端的 Systrace 如下所示
图中提交后还有 waiting for GPU completion 轨道。同步机制允许 GPU 工作尚未完成时先传递 Buffer 和 fence;queueBuffer 返回不能证明 GPU 完成,更不能证明已显示。等待区间也不等于 GPU 始终在执行。
SurfaceFlinger 端的 Systrace 如下所示
图中对应 Layer 的计数表示待消费队列项,入队增加、消费取走后减少;不是全部已分配 Buffer 数,也不是已经 GPU 就绪的 Buffer 数。左侧一次取走与右侧后续入队属于先后不同事件,不能把右侧新提交解释为已被左侧提前消费。
前面几步对应这张图的生产者一侧:
BufferQueue 部分
Triple Buffer 解读介绍了队列和槽位状态。BufferQueue 连接生产者与消费者;一个进程可使用多个队列,两端也可以同进程。它不是“每个有界面的进程一个队列”。

旧 SF 消费路径中,App 是生产者,SF 是消费者;BLAST 等路径的消费者位置不同,但这四步的职责仍可用于理解:
dequeueBuffer():生产者取得可用槽位,按尺寸、格式和 usage 等要求取得或复用 Buffer,写入前满足返回的同步依赖。queueBuffer():生产者提交 Buffer 和 acquire fence;提交不等于异步写入已经完成。acquireBuffer():消费者取得排队项;读取内容必须满足 acquire fence,或把该依赖继续传给后续硬件。releaseBuffer():消费者归还槽位并附带 release fence;生产者再次写入还要等此前读取完成。
这里传递的是 Buffer 的引用/句柄及同步信息,不是四次像素拷贝;队列是否替换旧项、允许同时持有多少 Buffer,也不是这张图能决定的。
SurfaceFlinger 部分
工作流程
在旧路径里,SF 按调度处理事务和候选 Buffer,决定是否需要一次合成;没有新内容不必每个 Vsync 都重复工作。图中可见 handlePageFlip → latchBuffer,随后进入 handleMessageRefresh:

下面代码对照 AOSP android-10.0.0_r1 的 SurfaceFlinger.cpp。onMessageReceived 只保留相关分支,省略了背压、显示模式切换、refreshNeeded 判断和 signalRefresh 等逻辑,不可作为完整实现复制;其后两个函数保留该 tag 的实现。
MessageQueue::INVALIDATE:处理事务、尝试更新图层,并按条件安排 REFRESH。MessageQueue::REFRESH:进入handleMessageRefresh。
1 | void SurfaceFlinger::onMessageReceived(int32_t what) NO_THREAD_SAFETY_ANALYSIS { |
handleMessageRefresh 可以按职责阅读:
- 准备:
preComposition()、rebuildLayerStacks()、calculateWorkingSet()。 - 按显示处理:
beginFrame()、prepareFrame()、调试闪烁处理和doComposition()。 - 后续处理:
logLayerStats()、postFrame()、postComposition(),以及合成类型、Vsync 调制和队列状态的更新。
最后一步包含提交后的同步和统计工作,函数返回不等于面板呈现已经完成。也不要把这些 CPU 方法的长度直接当作 GPU 时间。
Android 16 对照入口:在 android-16.0.0_r1 中,commit() 更新图层快照、处理事务并决定是否需要 composite;composite() 准备合成参数,再调用 CompositionEngine::present。后者对各 output 执行 prepare/present,部分 HWC present 调用还可转交其他线程并等待结果。新系统应从这些入口、实际轨道和设备源码追踪,不再要求出现旧版两个 Message 名称。这里仅是阅读入口,不包含全部显示模式、背压和多显示器分支。
显示系统的细节非常多,我这里就不一一展开了。如果只是想熟悉流程,先把 SurfaceFlinger 的主要工作逻辑看明白就好;如果工作就在这一块,再沿着对应版本的源码把这些分支和依赖逐个弄清楚。
掉帧
判断掉帧,先确认哪一层的哪一帧没有达到预期呈现时间,再沿依赖向前找原因:
- 核对刷新模式、目标帧率和帧身份。静止页面或低于屏幕刷新率的内容,没有每个 Vsync 都合成是正常情况。
- 有待更新内容却未及时合成时,检查 SF 调度、事务、锁存条件及此前帧的背压;再沿 CPU 调度、Binder、GPU/HWC fence 查明阻塞,而不是只看长 Slice。
- SF 合成了,也可能沿用目标 App 的旧 Buffer,或只更新了其他图层。队列计数为 0 可能是刚被取走,也可能尚未提交;它不能单独证明 App 出错,App 的延迟也可能来自下游背压。
- Android 12(API 31)起,在采集数据支持时用 FrameTimeline关联 App 的 SurfaceFrame 和 SF 的 DisplayFrame,比较 Expected/Actual、present type 和 jank type,再检查具体线程/硬件证据。Actual Timeline 不是 CPU Running 时间;缺失轨道也不能当作“没有掉帧”。
旧 Systrace 的对应分析见 Triple Buffer:掉帧判断。单凭本文裁剪截图无法重新统计完整掉帧率。
HWComposer 部分
Hardware Composer HAL根据图层状态和设备能力协商合成方案:
- Client composition:SurfaceFlinger 经 RenderEngine 合成相应图层,把 client target 交给 HWC。Android 16 RenderEngine支持 GL/Vulkan 后端选择,不能把 client 一律写成 GLES。
- Device composition:交由设备 HWC 实现处理。Overlay 是常见方式;可用平面、缩放、旋转、混合、格式等限制取决于硬件和驱动,不存在 Android 4.4 起统一“四个平面”的保证。
- 硬件合成可能减少 GPU 工作;静态内容的复用、内存带宽和混合成本也会影响策略。因此 client composition 本身不是性能故障,层数多也不能直接换算成掉帧数。
接口版本:下图是历史 android.hardware.graphics.composer@2.1-service 的 HIDL 路径。Android 13 起提供 AIDL composer3,旧 HIDL 2.1~2.4 已被官方标为 deprecated;但 Android 13 当时并未要求所有厂商立即切换。分析设备时仍需确认实际 HAL。

图中 SF 的 executeCommands 经 Binder 到 HWC,下面可见 Present/Commit/ATOMIC 等区间。选中的 642 μs 是图上的时间范围,不是已测得的 GPU 执行时间,也不是最终显示完成时间。HWC 返回后,硬件仍可等待 fence 或继续处理。
这对应原流程图的后半段:
分析时要分清:acquire fence 表示读取内容前的依赖,release fence 控制何时可安全复用旧 Buffer,display present fence 提供呈现完成的同步信息。比如 NDK API 29 的事务统计分别提供 present 和 previous-release fence;不支持 present fence 的设备可返回无效值,不能把它解释成零耗时。HAL 方法返回或 SF latch 时间都不等于 present fence 的信号时间。
HWC 相关等待可能影响后续帧,参见 Android 中的卡顿丢帧原因概述 - 系统篇。进一步阅读 Android P 图形显示系统(一)硬件合成 HWC2及其系列时,按 Android 9/P 的历史背景对照,不把旧接口名当作当前实现。
参考文章
- Android P 图形显示系统(一)硬件合成 HWC2(历史资料)
- Android P 图形显示系统(历史系列)
- SurfaceFlinger and WindowManager
- SurfaceFlinger 分析文档(历史资料)
关于我 && 博客
下面是个人的介绍和相关的链接,期望与同行的各位多多交流,三人行,则必有我师!
- 博主个人介绍 :里面有个人的微信和微信群链接。
- 本博客内容导航 :个人博客内容的一个导航。
- 个人整理和搜集的优秀博客文章 - Android 性能优化必知必会 :欢迎大家自荐和推荐 (微信私聊即可)
- Android性能优化知识星球:付费社群已于 2026 年停止运营,不再招收新成员;此处保留历史介绍。
一个人可以走的更快 , 一群人可以走的更远
