Crash(崩溃)的定义、分类与计算口径
聊稳定性,第一个要讲的就是 Crash。说白了,Crash 就是应用因未捕获的异常或系统级错误而意外终止。用户看到的是「App 突然没了」、或者直接回到桌面,严重的甚至整机重启。
我在项目中遇到过不少 Crash,但早期我其实没太区分「Java 崩溃」和「Native 崩溃」——总觉得都是崩溃,算一个数就行了。后来被线上问题教育了一顿:有一次大量用户反馈「解锁后屏幕黑几秒才恢复」,查了好久才发现是 System Server 里一个 Native 指针越界导致的。从那以后,我对 Crash 的分类就特别上心了。
1. Crash 的定义
从 Android 系统角度看,Crash 指 进程因不可恢复的错误而异常退出。这个错误可以来自 Java 层(比如 NullPointerException),也可以来自 Native 层(比如 SIGSEGV)。系统会捕获这些信号,生成 tombstone 文件(Native)或写入 DropBox(Java),最终弹出「App 已停止运行」的对话框。
💡 一个小细节:如果应用在前台崩溃,用户会直接感知;如果退到后台后崩溃(比如 Service 异常),有时用户不会立刻察觉到,但指标上仍然算一次 Crash。
2. Crash 类型:Java 与 Native
Java 和 Native 崩溃的捕获机制、堆栈格式、排查工具完全不同。你想想看,Java 崩溃我们能直接看到异常类型和调用链,而 Native 崩溃看到的是一堆寄存器值和信号码——处理起来像换了一门语言。
| 维度 | Java Crash | Native Crash |
|---|---|---|
| 触发原因 | 未捕获的 Java 异常(Throwable) | 信号机制(SIGSEGV、SIGABRT 等) |
| 常见场景 | NullPointer、IndexOutOfBounds、ClassCast、OOM | 指针野、堆破坏、JNI 滥用、系统库崩溃 |
| 捕获机制 | Java 层 UncaughtExceptionHandler | Linux 信号处理器 + debuggerd |
| 堆栈信息 | 完整的 Java 调用栈 | 寄存器 + 函数地址(需 addr2line 或 ndk-stack) |
| 错误文件 | DropBox / logcat | tombstone / logcat |
这里提个我踩过的坑:我一直以为只要监测 UncaughtExceptionHandler 就行了,直到有一天我发现一部分 Crash 根本没被捕获——原来是 Native 层的 SIGABRT 直接杀掉了进程,Java 层的 handler 连跑的机会都没有。所以做稳定性的朋友,一定要同时监控两种类型。
⚠️ 避坑指南:我曾经只统计 Java 层的「崩溃率」,结果线上明明有 0.5% 的 Native 崩溃,我看的指标却一直是 0.1%。后来花了三天才把 Native 采集渠道补上——别学我。
3. 崩溃率计算口径
定义清楚 Crash 之后,怎么算「崩溃率」?我见过很多团队口径不统一,导致两个版本的数据差了 2 倍。所以咱得把这几个口径掰清楚。
3.1 用户影响口径(用户级崩溃率)
公式很简单:
用户级崩溃率 = 发生至少一次 Crash 的用户数 / 日活跃或月活跃用户数 × 100%
这个口径反映的是 「有多少比例的用户受到了崩溃影响」,而不是「崩溃的严重程度」。我建议线上核心指标用这个,因为一个用户一天崩 10 次和崩 1 次,对用户体验的伤害程度是不一样的,但「受影响的用户数量」直接决定了口碑扩散范围。
3.2 会话口径(Session 崩溃率)
Session 崩溃率 = 导致 session 异常结束的 Crash 次数 / 总 Session 数 × 100%
这里要定义什么是 Session。一般从应用启动开始,到应用进入后台超过 30 秒(或主动退出)结束。如果一个 Session 内发生了 Crash,就算一次崩溃 Session。这个口径 能更好地衡量崩溃对用户使用流程的打断程度。
3.3 启动口径(Startup 崩溃率)
启动崩溃率 = 应用启动后 N 秒内发生 Crash 的次数 / 启动总次数 × 100%
N 通常取 5 秒或 10 秒。启动阶段的崩溃 对用户伤害最大——一打开就闪退,用户很可能直接卸载。
🔑 重点总结:我个人习惯按 「用户影响口径」+「Session 口径」双指标看。一个看广度,一个看频次。两个差了 3 倍以上时,说明少数用户重复崩溃得很厉害,需要排查「用户修复方案」。至于启动崩溃率,我通常单独挂在「第一屏监控」上。
哦对了,还有一种计算方式叫 「崩溃次数 / DAU」,这个不是真正的「率」,而是「人均崩溃次数」。某些游戏团队喜欢这么用,但我不推荐作为稳定性核心指标,容易稀释掉「用户受影响」这个关键信息。
4. 不同口径的适用场景
每个口径都不是万能的,我根据自己的实践经验列了个对比 —— 你根据业务选择合适的组合就好:
- 用户级崩溃率 —— 适用于版本质量看板、发布门禁。我们团队曾经定了个红线:用户级崩溃率 ≤ 0.2% 才能全量发布,超过的话就灰度回滚。
- Session 崩溃率 —— 适用于衡量「单次体验是否完整」。做广告或支付场景时特别重要,如果一个用户正在付款时崩溃,那不是数据丢失的问题,是收入损失。
- 启动崩溃率 —— 适用于新版本首次安装后的监控。我有个朋友团队发布了一个版本,启动崩溃率达到 5%,他们 2 小时内就回滚了——及时止损。
⚠️ 注意:计算 Session 崩溃率时,如果 Session 定义不统一(比如我们之前按前台 60 秒后台 10 秒),数据根本无法与行业对标。建议参考 Android Vitals 的 Session 标准:前台运行到后台超过 30 秒则切新 Session。
好了,Crash 的定义、两种类型、三种主要计算口径都讲清楚了。记住核心:不要只看一个数,至少要用户级 + Session 级双层监测。 至于怎么采集、怎么治理,后面的章节会展开。嗯,先消化这些吧。