系列文章目录:
- Android性能优化典范综述
- Android性能优化典范之Render Performance
- Android性能优化典范之Understanding Overdraw
- Android性能优化典范之Understanding VSYNC
- Android性能优化典范之Profile GPU Rendering
技术复核(2026-10-13):保留 2015 年的视频简介和九张示意图。正文已修正固定 60Hz、撕裂原因以及“VSync 复制后缓冲”的说法,并补充现代 Android 的帧率提示与分析入口。
当时的视频简介:
Unbeknown to most developers, there’s a simple hardware design that defines everything about how fast your application can draw things to the screen.
You may have heard the term VSYNC - VSYNC stands for vertical synchronization and it’s an event that happens every time your screen starts to refresh the content it wants to show you.
Effectively, VSYNC is the product of two components Refresh Rate (how fast the hardware can refresh the screen), and Frames Per Second (how fast the GPU can draw images), and in this video +Colt McAnlis walks through each of these topics, and discusses where VSYNC (and the 16ms rendering barrier) comes from, and why it’s critical to understand if you want a silky smooth application.
基本概念
想要理解界面为什么流畅或卡顿,需要先区分应用产帧、系统合成和屏幕呈现。VSync(垂直同步) 用来协调这些阶段的节奏,不是刷新率与 FPS 的数学乘积,也不是一个替应用完成绘制的操作。AOSP VSync 说明
原视频以 60Hz 为主。60Hz 对应约 16.67ms 的刷新间隔,90Hz 约 11.11ms,120Hz 约 8.33ms。间隔不能直接当作每个线程各自可用的完整预算;实际截止时间取决于流水线调度,后面再说。
在了解VSYNC之前,我们需要了解两个概念:
刷新率
刷新率用 Hz 表示屏幕的刷新节奏。支持的模式受硬件限制,但运行时不一定固定:系统可以根据内容、功耗策略和其他窗口选择刷新率。不要把 60Hz 当成所有 Android 设备的默认值。Android 帧率说明

帧速率
帧速率用 fps 表示每秒的帧数,但要说清楚计数位置:应用提交了多少帧、GPU 完成了多少帧、用户实际看到了多少个不同画面,可能不是同一个数。它也不是 GPU 的 MHz 时钟频率。
更高的帧率不总是更好的目标。连续交互需要稳定的节奏和较低延迟,视频则有自己的内容帧率;超出需要的工作会增加功耗。稳定的 30fps 视频在 60Hz 屏幕上每帧显示两次,可以是正常播放,不应仅因“重复了一帧”就判断为应用掉帧。

工作原理
应用生成图像,SurfaceFlinger 组织合成,显示系统把结果呈现出来。下面每一列是“生成内容到显示”的简化示意,省略了 CPU、队列、合成和同步;它不是完整 Android 时序,也不表示静止页面必须不停重绘。

刷新率和内容帧率可以不同,问题在于如何安排它们的时序。
产帧比刷新快:未显示的帧与撕裂要分开
下图用 100fps 与 75Hz 示意“产出的帧不一定全部显示”。具体哪些帧被丢弃、排队,或生产者在哪里被限速,取决于队列和呈现策略;不能从这个比例直接推出必然撕裂。

举个例子,你拍一张照片,旋转 5 度再拍一张,把两张照片的上下部分拼起来:


上、下部分来自不同时间,接缝处出现错位,这就是图中要解释的 tearing(撕裂)。在扫描输出与更新未正确同步时,一次扫描可能读到不同帧的内容,例如读取过程中正在使用的图像被改写,或显示源切换得太早。帧率与刷新率不同并不是充分条件;即使平均数相同,错误的同步也可能撕裂。

Android 图形栈通过缓冲区所有权、同步 fence 和呈现调度来协调读写。以 BufferQueue 为例,生产者 dequeue 可用 buffer,提交后由消费者 acquire,使用完成再 release;队列传递的是 buffer 句柄,BufferQueue 本身不复制像素内容。可用 buffer 数量由双方约束和具体路径决定,不能统一写死成双缓冲、三缓冲或四缓冲。
buffer 已入队也不代表 GPU 一定画完了。同步 fence表达读写完成的依赖:消费者读取前要满足生产者写完的条件,生产者复用前也要满足消费者用完的条件。合成可能仍产生新的输出 buffer,“队列不复制”不等于整个图形流水线从来没有拷贝。

图中的 Back Buffer / Frame Buffer 是教学模型。应把它理解成“避免边读边覆盖同一幅待显示图像”,而不是“每次 VSync 都把后缓冲复制到前缓冲”。VSync 协调应用、SurfaceFlinger 与显示的时序,这些阶段可以流水并行,唤醒时机也可以错开;不是 GPU 每画完一帧都原地等待同一个硬件中断。
普通 View 动画和遍历由框架接入 Choreographer(Android 4.1 / API 16+)调度。应用通常使用动画 API、View.postOnAnimation() 等入口,不需要自己写 sleep(16) 来模拟 VSync。
刷新比产帧快:先区分正常重复和错过期限
没有可用的新帧时,显示系统可以继续呈现旧内容。原图的 58fps / 60Hz 例子只是示意;每个新画面持续多久、是否偏离预期呈现时机,比平均 FPS 更能解释用户看到的卡顿。

主动选择较低的内容帧率,与连续动画突然漏掉预期帧,是不同情况。后者可能表现为卡顿或延迟,但不应笼统叫“闪屏”。原因也未必是 GPU 画得慢:主线程阻塞、线程没及时被调度、渲染、队列与合成都可能影响结果。
Android 11 / API 30+ 的 Surface.setFrameRate(float, int) 可以表达内容的期望帧率。它给系统选择显示模式提供提示,不保证刷新率一定切换,也不会自动让应用按该频率产帧或替应用限速。普通 UI 仍应跟随框架调度;视频和自建渲染路径还需正确安排呈现时机。
排查时在 Android 12 / API 31+ 使用 Perfetto FrameTimeline,对照 expected / actual timeline 和 jank 原因,再关联同一帧的线程、GPU 与 SurfaceFlinger 证据。是否有 fence、调度等详细轨道取决于实际采集配置和设备;不要把 Choreographer 回调次数当作实际显示帧数,也不要只凭“某段代码超过 16ms”给出结论。
Perf Matters
keep calm, profile your code, and always remember, Perf Matters
关于我 && 博客
下面是个人的介绍和相关的链接,期望与同行的各位多多交流,三人行,则必有我师!
- 博主个人介绍 :里面有个人的微信和微信群链接。
- 本博客内容导航 :个人博客内容的一个导航。
- 个人整理和搜集的优秀博客文章 - Android 性能优化必知必会 :欢迎大家自荐和推荐 (微信私聊即可)
- Android性能优化知识星球:付费社群已于 2026 年停止运营,不再招收新成员;此处保留历史介绍。
一个人可以走的更快 , 一群人可以走的更远
