帧率抖动基础:FPS、Jank与帧丢失
做UI性能优化,帧率是你绕不开的门槛。我刚入行时觉得App能跑就行,直到第一次被用户反馈“滑动卡成PPT”,才真正意识到帧率这玩意儿有多重要。今天我们就来拆解帧率抖动最底层的东西——FPS、Jank、帧丢失到底是什么。
1. FPS:每秒帧数的真相
FPS(Frames Per Second)就是每秒刷新的画面数量。Android系统默认是60fps,也就是每16.67ms要生成一帧。如果超过这个时间,帧率就会掉。
但FPS有个问题——它只是个平均值。你想想看,一个App前900ms卡成狗,最后100ms狂飙几百帧,平均下来还是60fps。这能说明流畅吗?当然不能。所以业内很少只看FPS了,更关注帧间隔(Frame Interval)和Jank。
核心要点: FPS平均值会掩盖卡顿。关键看每一帧的耗时是否稳定在16.67ms以内。
2. Jank:真正让你感知的卡顿
Jank是业界定义的标准。简单说:如果一帧的绘制时间超过了垂直同步周期(比如16.67ms),导致下一帧没能按时显示,用户就会感觉到“顿一下”。
我个人习惯用Choreographer的FrameCallback来抓每一帧的时间。有一次我在优化一个电商App的首页,发现平均FPS有55,但就是感觉滑动不跟手。抓了Jank数据一看,每隔20帧就有一帧耗时40ms以上,这种突然的“刺头”才是罪魁祸首。
| 指标 | 含义 | 可接受阈值 |
|---|---|---|
| FPS | 每秒帧数均值 | ≥55 |
| Frame Interval | 两帧之间的实际时间差 | ≤16.67ms |
| Jank Count | 超过阈值的帧数量 | 每分钟≤3次 |
| Frame Drop | 连续丢帧的严重程度 | 不允许连续丢2帧以上 |
3. 帧丢失:到底丢了什么?
帧丢失不是真的“丢掉”了你看到的画面。而是系统检测到某一帧来不及渲染完,就直接跳过了。比如第5帧没画完,画面会重复显示第4帧直到第6帧画好。用户感知就是画面“卡住”了一瞬间。
为什么会丢帧?原因很多。WorkManager任务阻塞就是其中一个隐藏杀手——当后台任务占用了主线程的CPU时间片,渲染线程就饿死了。我曾经在一个地图App里发现,WorkManager每小时同步一次数据,同步那一瞬间帧率会掉到30以下,持续几百毫秒。用户刚好在滑动地图,就会触发Jank。
避坑指南: 我曾经以为WorkManager在后台线程执行不会影响UI,但忽略了任务启动时可能触发ContentProvider初始化、数据库锁竞争等,导致主线程被阻塞。一定要用工具抓帧耗时曲线,别靠感觉。
4. 三者的关系
FPS、Jank、帧丢失是三个不同维度:
- FPS告诉你整体快慢,但会掩盖毛刺
- Jank告诉你毛刺的频率,是用户感知的核心
- 帧丢失告诉你系统应对不及的严重程度
我们做WorkManager治理,目的就是消除那些“突然冒出来的大帧”,把Jank Count压到最低。
小技巧: 用Android Profiler的“Frame Rendering”局部功能,直接看每帧的柱状图。凡是超过绿色线的柱子,都是你的敌人。
5. 治理的起点
理解了这三个概念,你就知道WorkManager治理要做什么了。不是把FPS从55提到60,而是把那些“随机出现的100ms大帧”找出来干掉。我个人习惯先跑一个长时间自动化测试,抓出Jank热点,再去定位到底是哪个WorkManager任务在作祟。