3. 卡顿与ANR的定义及分类
在 Android 稳定性工程中,很多团队把“卡”“慢”“无响应”“闪退”“内存爆”混在一起讨论,导致定位方向完全跑偏。实际上,卡顿、掉帧、Jank、ANR、Crash、OOM、Watchdog 分属不同的故障域:有的属于渲染性能问题,有的属于主线程调度问题,有的属于进程级崩溃,有的属于系统级守护触发。只有先把概念边界划清,才能选对监控指标、日志入口和修复手段。
一、概念辨析:七个高频术语的边界
卡顿(Lag / Stutter)是用户主观感受的统称,指界面响应迟缓、滑动不跟手、动画不连贯。它是一个体验结论,不是技术根因。卡顿可能由掉帧引起,也可能由主线程耗时任务、I/O 阻塞、锁竞争引起。
掉帧(Dropped Frame)是渲染管线的客观事实:在 60Hz 屏幕上,每帧预算约 16.6ms;在 120Hz 屏幕上约 8.3ms。当某一帧未能在 VSync 周期内完成合成,这一帧就被丢弃,表现为画面停顿或跳跃。掉帧是可量化指标,常用 FrameMetrics、JankStats、gfxinfo 采集。
Jank 通常指“可感知的帧延迟”,比单纯掉帧更贴近用户感受。Android 官方用 Jank 描述帧耗时超过阈值导致的视觉不连续。注意:掉帧不一定被用户感知,Jank 强调可感知性。
ANR(Application Not Responding)是主线程在规定时间内未完成输入事件或广播处理,系统弹出“应用无响应”对话框。典型触发条件:输入事件 5 秒未处理、前台广播 10 秒、后台广播 60 秒、ContentProvider 发布 10 秒等。ANR 是系统判定,不是用户主观感受。
Crash 是进程异常终止,包括 Java 未捕获异常、Native 信号(SIGSEGV、SIGABRT)、系统杀进程等。Crash 的直接证据是 tombstone 或 AndroidRuntime 日志。
OOM 分两层:Java 层 OutOfMemoryError 是堆内存分配失败;LMK(Low Memory Killer)触发的进程被杀是系统内存压力下的回收行为。前者是应用崩溃,后者常表现为“后台被杀”“冷启动变慢”。
Watchdog 是系统级或应用级守护机制。系统 Watchdog 监控关键服务(如 system_server)是否卡死,超时则重启系统;应用也可自建 Watchdog 监控主线程心跳。Watchdog 触发往往意味着更严重的系统性阻塞。
二、用户感知指标:从“我觉得卡”到“数据能证明”
用户不会说“我的帧耗时 32ms”,他们只会说“滑动像幻灯片”。因此需要把主观感受映射为可采集指标:
- 帧耗时(Frame Duration):单帧从开始到完成的时间,超过 VSync 周期即掉帧。
- 掉帧率(Dropped Frame Rate):掉帧数 / 总帧数,适合衡量滑动、动画场景。
- Jank 率:可感知卡顿帧占比,Android 官方推荐用
JankStats采集。 - 输入延迟(Input Latency):从触摸事件到画面反馈的时间,直接影响“跟手”感。
- ANR 率:ANR 次数 / 启动次数或使用时长,属于稳定性核心指标。
- Crash 率 / OOM 率:崩溃与内存溢出占比,通常按版本、机型、场景维度聚合。
- Watchdog 触发次数:系统级或应用级守护触发,代表严重阻塞。
三、案例场景:同一次“卡死”如何被误判
某电商 App 在低端机上出现“点击商品详情后卡住 3 秒,然后闪退”。团队最初按“卡顿”优化,只做了布局层级裁剪,问题依旧。重新按故障域拆解:
- 用户点击后主线程执行了同步数据库查询,阻塞超过 5 秒,系统先判定 ANR;
- ANR 期间应用仍在分配大图,触发 OOM,进程被 LMK 杀死;
- 用户看到的是“卡住然后闪退”,但根因是主线程 I/O + 内存峰值,不是渲染掉帧。
如果一开始只盯帧率,就会错过 traces.txt 中的主线程栈和 meminfo 中的内存曲线。正确做法是:先看 ANR 日志确认主线程阻塞点,再看 OOM 日志确认内存峰值,最后才评估渲染层是否需要优化。
四、误区与复盘:常见概念混淆清单
| 误区 | 错误理解 | 正确认知 |
|---|---|---|
| 把 ANR 当卡顿 | ANR 只是“卡得久一点” | ANR 是系统判定,有明确超时阈值和日志证据 |
| 把掉帧当 Jank | 掉帧数高就一定卡 | 掉帧是客观事实,Jank 强调用户可感知 |
| 把 OOM 当 Crash | 都是闪退,不用区分 | OOM 有内存分配失败或 LMK 杀进程证据,修复方向不同 |
| 把 Watchdog 当 ANR | 都是无响应 | Watchdog 监控系统关键服务,触发后果更严重 |
| 只看平均值 | 平均帧率 55fps 就合格 | 要看 P95/P99 帧耗时和 Jank 率,长尾才是体验杀手 |
五、采集入口与关键参数
不同故障域对应不同采集命令和日志入口,下面给出常用组合:
# 帧与 Jank 采集
adb shell dumpsys gfxinfo com.example.app framestats
adb shell dumpsys SurfaceFlinger --latency
# ANR 日志与主线程栈
adb shell cat /data/anr/traces.txt
adb bugreport
# Crash / Native 崩溃
adb logcat -b crash
adb shell ls /data/tombstones/
# 内存与 OOM
adb shell dumpsys meminfo com.example.app
adb shell dumpsys activity lru
# 系统 Watchdog
adb logcat -s Watchdog
关键参数上,60Hz 屏幕单帧预算 16.6ms,120Hz 为 8.3ms;ANR 输入事件超时 5s,前台广播 10s,后台广播 60s;这些阈值是判断“是否越界”的硬标准。实际工程中,建议把帧耗时 P95、Jank 率、ANR 率、Crash 率、OOM 率、Watchdog 触发次数作为稳定性看板的核心六项指标,按版本和机型维度持续跟踪。