第2章 帧率测量指标选择:FPS、Jank Count、FrameTimeline、Dropped Frames、Frame Duration分布
2.1 现象识别 — 不同卡顿表象对应不同测量指标
帧率回归并非单一表现,用户感知的“卡顿”可能来自以下五种典型现象。选择错误的测量指标会导致误判或漏报,因此必须先识别表象,再匹配指标。
| 用户感知现象 | 典型描述 | 最匹配的测量指标 |
|---|---|---|
| 整体画面迟钝、滑动跟手性差 | “界面总是慢半拍” | FPS(平均帧率) |
| 偶发性画面跳跃、动画不连贯 | “突然跳一下,然后又好了” | Jank Count(卡顿次数) |
| 特定页面或动画卡死几毫秒 | “点下去等了一会才有反应” | FrameTimeline(单帧各阶段耗时) |
| 画面撕裂、部分区域刷新延迟 | “屏幕好像分成了两块” | Dropped Frames(丢帧计数) |
| 性能不稳定,时好时坏 | “一会儿流畅一会儿卡” | Frame Duration 分布(耗时分布特征) |
判断规则:当用户反馈“卡”时,首先归类到上述五种现象之一。若反馈模糊(如“不好用”),优先采集 Jank Count 和 Frame Duration 分布,两者能覆盖 80% 的帧率回归场景。
2.2 数据采集 — 五大指标的标准获取路径
每个指标都有对应的 AOSP 工具链和命令行入口。采集时需固定测试场景、时长(至少 10 秒)、环境温度,避免数据漂移。
| 指标 | 采集工具 / 命令 | 数据粒度 |
|---|---|---|
| FPS | |
每秒平均帧数 |
| Jank Count | |
卡顿事件次数 |
| FrameTimeline | |
每帧的 App / Buffer / GPU 耗时 |
| Dropped Frames | |
累计丢帧数量 |
| Frame Duration 分布 | |
帧耗时区间分布(如 0-16ms, 16-32ms 等) |
采集陷阱:仅读取 FPS 可能掩盖“高帧率但高抖动”的问题。例如 FPS=58 但 Jank Count=200 次/分钟,用户体验依然很差。必须组合指标交叉验证。
2.3 原因定位 — 指标组合故障树
单一指标只能描述“是什么”,无法回答“为什么”。使用以下故障树,通过指标组合快速定位根因。
故障树判断规则:
① FPS 正常 & Jank Count 高 → 大概率是单帧异常耗时(查 FrameTimeline)。
② FPS 偏低 & Dropped Frames 高 → 渲染管线瓶颈(查 GPU/Overdraw)。
③ Frame Duration 分布呈双峰 → 存在两种帧耗时模式(检查后台任务或 GC 触发)。
④ FrameTimeline 显示 App 阶段耗时翻倍 → 主线程消息队列堆积(查 Handler/MethodTrace)。
① FPS 正常 & Jank Count 高 → 大概率是单帧异常耗时(查 FrameTimeline)。
② FPS 偏低 & Dropped Frames 高 → 渲染管线瓶颈(查 GPU/Overdraw)。
③ Frame Duration 分布呈双峰 → 存在两种帧耗时模式(检查后台任务或 GC 触发)。
④ FrameTimeline 显示 App 阶段耗时翻倍 → 主线程消息队列堆积(查 Handler/MethodTrace)。
| 指标组合 | 典型根因 | 下一步排查方向 |
|---|---|---|
| FPS↓ + Jank Count↑ | CPU 频率限制 / 调度延迟 | 检查 cpufreq / sched_stat |
| FPS↑ + Jank Count↑ | 渲染管线频繁丢帧 | 检查 GPU 频率 / SurfaceFlinger |
| FrameTimeline App 阶段异常 | 主线程耗时操作(布局/绘制) | 抓取 Systrace 聚焦 UI Thread |
| Dropped Frames 集中在特定组件 | 自定义 View 过度绘制 | 开启 GPU 渲染模式分析 |
| Frame Duration 右尾过长 | 周期性 GC 或 SO 加载 | 检查 logcat GC 日志 / 动态链接 |
2.4 修复动作 — 按指标类型定向优化
根据故障树定位到的根因,执行对应的修复策略。以下按指标维度给出典型修复动作。
| 指标异常特征 | 典型修复动作 |
|---|---|
| FPS 整体偏低(< 55fps) | 检查 CPU 调度策略、降低主线程负载、开启 GPU 渲染加速 |
| Jank Count 单次 > 3 次/秒 | 异步化耗时操作、预加载资源、减少反射调用 |
| FrameTimeline App 阶段 > 12ms | 精简布局层级、使用异步 Inflate、优化自定义 View 的 onDraw |
| Dropped Frames 累计 > 5% 总帧数 | 排查 Overdraw、减少 GPU 纹理上传、启用硬件缓冲队列 |
| Frame Duration 分布出现 > 50ms 长尾 | 排查 GC 频率、检查一帧内是否发生类加载或 IO 操作 |
修复优先级建议:先解决 FrameTimeline 中 App 阶段的单帧耗时异常(通常影响面最大),再处理 Dropped Frames 类渲染瓶颈。FPS 和 Jank Count 作为验证指标,不要直接作为优化目标。
2.5 复测 — 同一场景下对比指标基线
复测必须使用与“现象识别”阶段完全相同的测试场景、操作路径和环境温度。对比修复前后的五个指标变化,确认回归是否消除。
| 指标 | 复测通过标准(示例基线) | 检查要点 |
|---|---|---|
| FPS | ≥ 58 fps(与基线偏差 < 2%) | 排除温升降频导致的 FPS 波动 |
| Jank Count | ≤ 基线值 + 5% | 关注峰值 Jank 是否消除 |
| FrameTimeline | App 阶段 ≤ 10ms,Buffer 阶段 ≤ 4ms | 检查长帧(≥ 20ms)是否消失 |
| Dropped Frames | 丢帧率 ≤ 1% | 检查连续丢帧是否消除 |
| Frame Duration 分布 | 90% 帧耗时 ≤ 20ms | 检查右尾长度是否缩短 |
复测陷阱:不要只测一次。连续采集 3 轮(每轮 10 秒),取中位数。如果某一轮指标突然恶化,说明修复不稳定,需要排查偶发干扰(如网络请求、后台同步)。
2.6 预防 — 建立指标基线 + 自动化看护
在版本发布 pipeline 中嵌入指标采集步骤,每次 AOSP 版本更新后自动对比基线,从源头拦截帧率回归。
- 基线固化:将当前稳定版本的五个指标值记录为 JSON 配置文件,纳入版本管理。
- 自动化看门狗:在 CI/CD 中执行
自动判定回归。python3 frame_regression.py --baseline baseline.json --current result.json - 定期校准:每月使用同一台物理设备、同一场景复测基线,排除系统级漂移。
- 告警规则:任意指标超过基线阈值(如 FPS 下降 > 3% 或 Jank Count 上升 > 10%)自动触发回归标记。
预防最佳实践:将五个指标合并为一个“帧率健康分”(FPS × 0.3 + (1 - Jank Rate) × 0.25 + ...),单分数下降即告警,降低多指标人工分析成本。