Android 崩溃优化与应用稳定性全解析:Crash、ANR、OOM 到线上治理

线上最难受的稳定性问题,往往不是"某个空指针崩了"这么简单。更真实的情况是:某个版本刚灰度,Crash 率往上跳,ANR 也跟着涨;用户评论里说"打开就闪退""点一下没反应""页面卡死";你打开日志平台,Java 崩溃、Native 崩溃、OOM、主线程阻塞、启动失败混在一起,像一团线。

一句话结论: 崩溃优化不是到处加 try-catch,应用稳定性也不是只盯 Crash 率;真正可持续的做法,是把 Crash、ANR、OOM、Native Crash、卡顿和启动失败拆成不同故障类型,建立采集、归因、止血、修复、验证和回归防线。

本文以 Android 应用开发侧的稳定性治理为主,重点讲 Java Crash、Native Crash、ANR、OOM 和线上闭环。Android Vitals 的阈值、ANR 诊断、ApplicationExitInfo 等信息按 2026-10-09 查询的官方文档整理;具体数据口径和 Play Console 展示可能随 Google Play 政策更新,落项目时以当前控制台和官方文档为准。

0. 先把稳定性问题分清楚

很多团队一开会就说"线上不稳定",但这个词太大了。你要先问一句:到底是哪一种不稳定?

类型 用户感受 常见根因 常见证据
Java Crash 闪退 空指针、越界、非法状态、线程异常 Java 堆栈、设备信息、业务现场
Native Crash 闪退 C/C++ 越界、空指针、SIGSEGV、SIGABRT tombstone、信号、native 堆栈
ANR 卡死、点不动 主线程阻塞、锁等待、Binder 卡住、I/O 卡住 ANR traces、主线程堆栈、Perfetto
OOM 闪退或进程被杀 图片过大、泄漏、缓存失控、native 内存膨胀 OOM 堆栈、heap dump、meminfo
启动失败 打不开、白屏后退出 Provider、Application、首屏初始化异常 启动埋点、Crash 堆栈、ANR 信息
卡顿 不流畅 主线程长任务、布局过重、频繁 GC Frame metrics、Perfetto、卡顿堆栈

这几类问题不能用一套方法硬套。Crash 要看异常和堆栈,ANR 要看主线程为什么没响应,OOM 要看内存增长和对象持有,Native Crash 要看信号和 tombstone。
#mermaid-svg-85nGaJFjPoNHgru1{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-85nGaJFjPoNHgru1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-85nGaJFjPoNHgru1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-85nGaJFjPoNHgru1 .error-icon{fill:#552222;}#mermaid-svg-85nGaJFjPoNHgru1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-85nGaJFjPoNHgru1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-85nGaJFjPoNHgru1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-85nGaJFjPoNHgru1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-85nGaJFjPoNHgru1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-85nGaJFjPoNHgru1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-85nGaJFjPoNHgru1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-85nGaJFjPoNHgru1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-85nGaJFjPoNHgru1 .marker.cross{stroke:#333333;}#mermaid-svg-85nGaJFjPoNHgru1 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-85nGaJFjPoNHgru1 p{margin:0;}#mermaid-svg-85nGaJFjPoNHgru1 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-85nGaJFjPoNHgru1 .cluster-label text{fill:#333;}#mermaid-svg-85nGaJFjPoNHgru1 .cluster-label span{color:#333;}#mermaid-svg-85nGaJFjPoNHgru1 .cluster-label span p{background-color:transparent;}#mermaid-svg-85nGaJFjPoNHgru1 .label text,#mermaid-svg-85nGaJFjPoNHgru1 span{fill:#333;color:#333;}#mermaid-svg-85nGaJFjPoNHgru1 .node rect,#mermaid-svg-85nGaJFjPoNHgru1 .node circle,#mermaid-svg-85nGaJFjPoNHgru1 .node ellipse,#mermaid-svg-85nGaJFjPoNHgru1 .node polygon,#mermaid-svg-85nGaJFjPoNHgru1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-85nGaJFjPoNHgru1 .rough-node .label text,#mermaid-svg-85nGaJFjPoNHgru1 .node .label text,#mermaid-svg-85nGaJFjPoNHgru1 .image-shape .label,#mermaid-svg-85nGaJFjPoNHgru1 .icon-shape .label{text-anchor:middle;}#mermaid-svg-85nGaJFjPoNHgru1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-85nGaJFjPoNHgru1 .rough-node .label,#mermaid-svg-85nGaJFjPoNHgru1 .node .label,#mermaid-svg-85nGaJFjPoNHgru1 .image-shape .label,#mermaid-svg-85nGaJFjPoNHgru1 .icon-shape .label{text-align:center;}#mermaid-svg-85nGaJFjPoNHgru1 .node.clickable{cursor:pointer;}#mermaid-svg-85nGaJFjPoNHgru1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-85nGaJFjPoNHgru1 .arrowheadPath{fill:#333333;}#mermaid-svg-85nGaJFjPoNHgru1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-85nGaJFjPoNHgru1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-85nGaJFjPoNHgru1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-85nGaJFjPoNHgru1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-85nGaJFjPoNHgru1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-85nGaJFjPoNHgru1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-85nGaJFjPoNHgru1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-85nGaJFjPoNHgru1 .cluster text{fill:#333;}#mermaid-svg-85nGaJFjPoNHgru1 .cluster span{color:#333;}#mermaid-svg-85nGaJFjPoNHgru1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-85nGaJFjPoNHgru1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-85nGaJFjPoNHgru1 rect.text{fill:none;stroke-width:0;}#mermaid-svg-85nGaJFjPoNHgru1 .icon-shape,#mermaid-svg-85nGaJFjPoNHgru1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-85nGaJFjPoNHgru1 .icon-shape p,#mermaid-svg-85nGaJFjPoNHgru1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-85nGaJFjPoNHgru1 .icon-shape .label rect,#mermaid-svg-85nGaJFjPoNHgru1 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-85nGaJFjPoNHgru1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-85nGaJFjPoNHgru1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-85nGaJFjPoNHgru1 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 线上稳定性问题
Crash
ANR
OOM
卡顿
启动失败
Java 异常
Native 信号
主线程阻塞
内存失控
帧耗时过长
Provider 和 Application

我的经验是,稳定性治理最怕"混着修"。比如 ANR 上升时,只看 Crash 平台;OOM 频发时,只在业务代码外面套 try-catch;Native Crash 没符号表,却硬猜 Java 层代码。这种处理会很累,而且很容易修错。

1. Android Vitals 先给我们一个线上口径

Google Play 的 Android Vitals 会跟踪影响用户体验的稳定性指标。官方文档里,用户感知 Crash 率和用户感知 ANR 率都有 bad behavior 阈值。

按 2026-10-09 查询到的官方说明:

指标 整体 bad behavior 阈值 单设备型号 bad behavior 阈值 说明
用户感知 Crash 率 1.09% 8% 统计每天至少遇到一次用户感知 Crash 的日活用户占比
用户感知 ANR 率 0.47% 8% 统计每天至少遇到一次用户感知 ANR 的日活用户占比

这里的"用户感知"很关键。不是每个后台异常都同等重要,真正影响评分和口碑的,通常是用户正在使用时发生的闪退、卡死、启动失败。

但项目内部不要只看一个总 Crash 率。更有用的指标至少包括:

指标 为什么要看
Crash 率 版本是否稳定的基础指标
ANR 率 交互卡死,用户体感很差
启动 Crash 率 影响打开 App,是最高优先级
启动 ANR 率 常由 Provider、Application、首屏初始化引起
影响用户数 评估事故面,比单纯次数更接近用户损伤
Top 问题占比 判断是少数大问题还是很多小问题
新增问题数 识别版本引入的新坑
修复后回归率 看治理是否真的闭环

稳定性治理不是"崩了再修"。它应该像性能一样有基线,有门禁,有灰度,有回滚。

2. Java Crash:别把 try-catch 当护身符

Java Crash 最常见,也最容易被误解。比如:

kotlin 复制代码
val name = user.profile.name

线上某个用户的 profile 为空,就直接 NullPointerException。这种问题看起来很简单,但如果发生在启动页、首页、支付结果页,就可能变成大面积事故。

Java Crash 的采集核心是默认未捕获异常处理器:

kotlin 复制代码
// 简化教学版:说明链路,不是完整生产实现。
class CrashReporter(
    private val previous: Thread.UncaughtExceptionHandler?
) : Thread.UncaughtExceptionHandler {

    override fun uncaughtException(thread: Thread, throwable: Throwable) {
        val report = buildReport(thread, throwable)
        writeToLocalFile(report)
        uploadIfPossible(report)

        previous?.uncaughtException(thread, throwable)
            ?: kotlin.system.exitProcess(10)
    }
}

fun installCrashReporter() {
    val previous = Thread.getDefaultUncaughtExceptionHandler()
    Thread.setDefaultUncaughtExceptionHandler(CrashReporter(previous))
}

这里要注意两件事。

第一,采集器不是让 App "崩了也继续跑"。未捕获异常通常说明当前状态已经不可信。你可以记录现场,但不要盲目吞掉异常继续执行,否则可能从一次明确崩溃变成数据错乱、支付状态错误、页面半死不活。

第二,Crash 报告不能只存堆栈。一个能定位问题的报告,至少要有:

信息 作用
异常类型和堆栈 判断崩溃点
线程名 区分主线程、后台线程、线程池
App 版本和构建号 定位引入版本
Android 版本和设备型号 判断系统或机型相关
页面路径和业务动作 复现路径
用户状态和灰度配置 判断是否只影响某类用户
最近日志和关键埋点 还原崩溃前发生了什么
混淆映射版本 反混淆堆栈

#mermaid-svg-HGw8wsYad8RNgmSp{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-HGw8wsYad8RNgmSp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HGw8wsYad8RNgmSp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HGw8wsYad8RNgmSp .error-icon{fill:#552222;}#mermaid-svg-HGw8wsYad8RNgmSp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HGw8wsYad8RNgmSp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HGw8wsYad8RNgmSp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HGw8wsYad8RNgmSp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HGw8wsYad8RNgmSp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HGw8wsYad8RNgmSp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HGw8wsYad8RNgmSp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HGw8wsYad8RNgmSp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HGw8wsYad8RNgmSp .marker.cross{stroke:#333333;}#mermaid-svg-HGw8wsYad8RNgmSp svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HGw8wsYad8RNgmSp p{margin:0;}#mermaid-svg-HGw8wsYad8RNgmSp .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-HGw8wsYad8RNgmSp .cluster-label text{fill:#333;}#mermaid-svg-HGw8wsYad8RNgmSp .cluster-label span{color:#333;}#mermaid-svg-HGw8wsYad8RNgmSp .cluster-label span p{background-color:transparent;}#mermaid-svg-HGw8wsYad8RNgmSp .label text,#mermaid-svg-HGw8wsYad8RNgmSp span{fill:#333;color:#333;}#mermaid-svg-HGw8wsYad8RNgmSp .node rect,#mermaid-svg-HGw8wsYad8RNgmSp .node circle,#mermaid-svg-HGw8wsYad8RNgmSp .node ellipse,#mermaid-svg-HGw8wsYad8RNgmSp .node polygon,#mermaid-svg-HGw8wsYad8RNgmSp .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HGw8wsYad8RNgmSp .rough-node .label text,#mermaid-svg-HGw8wsYad8RNgmSp .node .label text,#mermaid-svg-HGw8wsYad8RNgmSp .image-shape .label,#mermaid-svg-HGw8wsYad8RNgmSp .icon-shape .label{text-anchor:middle;}#mermaid-svg-HGw8wsYad8RNgmSp .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-HGw8wsYad8RNgmSp .rough-node .label,#mermaid-svg-HGw8wsYad8RNgmSp .node .label,#mermaid-svg-HGw8wsYad8RNgmSp .image-shape .label,#mermaid-svg-HGw8wsYad8RNgmSp .icon-shape .label{text-align:center;}#mermaid-svg-HGw8wsYad8RNgmSp .node.clickable{cursor:pointer;}#mermaid-svg-HGw8wsYad8RNgmSp .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-HGw8wsYad8RNgmSp .arrowheadPath{fill:#333333;}#mermaid-svg-HGw8wsYad8RNgmSp .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-HGw8wsYad8RNgmSp .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-HGw8wsYad8RNgmSp .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HGw8wsYad8RNgmSp .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-HGw8wsYad8RNgmSp .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HGw8wsYad8RNgmSp .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-HGw8wsYad8RNgmSp .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-HGw8wsYad8RNgmSp .cluster text{fill:#333;}#mermaid-svg-HGw8wsYad8RNgmSp .cluster span{color:#333;}#mermaid-svg-HGw8wsYad8RNgmSp div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-HGw8wsYad8RNgmSp .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-HGw8wsYad8RNgmSp rect.text{fill:none;stroke-width:0;}#mermaid-svg-HGw8wsYad8RNgmSp .icon-shape,#mermaid-svg-HGw8wsYad8RNgmSp .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HGw8wsYad8RNgmSp .icon-shape p,#mermaid-svg-HGw8wsYad8RNgmSp .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-HGw8wsYad8RNgmSp .icon-shape .label rect,#mermaid-svg-HGw8wsYad8RNgmSp .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HGw8wsYad8RNgmSp .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-HGw8wsYad8RNgmSp .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-HGw8wsYad8RNgmSp :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 未捕获异常
UncaughtExceptionHandler
采集 Java 堆栈
采集设备和业务现场
写入本地文件
下次启动或当前进程尝试上传
稳定性平台聚合
分配 owner 修复

真正的优化不是给所有地方套 try-catch,而是按错误类型处理:

问题 更好的处理
空指针 数据模型明确可空边界,入口做校验
数组越界 UI 下标和数据源长度绑定,异步刷新时防过期
非法状态 状态机收敛,避免任意状态调用任意方法
JSON 解析失败 兼容字段缺失、类型错误和服务端灰度
Activity 已销毁回调 生命周期感知取消,回调前检查状态
三方 SDK 异常 包装边界和降级策略,不让 SDK 拖垮主流程

一个小意见:核心链路不要过度依赖"大兜底"。兜底可以保护用户,但兜底太多会让问题沉到水下。真正要做的是让错误可见、可归因、可修复。

3. Native Crash:不是 Java 堆栈能救的

Native Crash 常见于音视频、图片处理、地图、WebView 扩展、游戏引擎、加密库、算法库等场景。它不一定会给你熟悉的 Java 异常,而是以信号形式出现,比如 SIGSEGV、SIGABRT。

Native Crash 的关键证据通常是 tombstone 和 native 堆栈。
#mermaid-svg-AHdwQGT6M4s7TyFP{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-AHdwQGT6M4s7TyFP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-AHdwQGT6M4s7TyFP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-AHdwQGT6M4s7TyFP .error-icon{fill:#552222;}#mermaid-svg-AHdwQGT6M4s7TyFP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-AHdwQGT6M4s7TyFP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-AHdwQGT6M4s7TyFP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-AHdwQGT6M4s7TyFP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-AHdwQGT6M4s7TyFP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-AHdwQGT6M4s7TyFP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-AHdwQGT6M4s7TyFP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-AHdwQGT6M4s7TyFP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-AHdwQGT6M4s7TyFP .marker.cross{stroke:#333333;}#mermaid-svg-AHdwQGT6M4s7TyFP svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-AHdwQGT6M4s7TyFP p{margin:0;}#mermaid-svg-AHdwQGT6M4s7TyFP .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-AHdwQGT6M4s7TyFP .cluster-label text{fill:#333;}#mermaid-svg-AHdwQGT6M4s7TyFP .cluster-label span{color:#333;}#mermaid-svg-AHdwQGT6M4s7TyFP .cluster-label span p{background-color:transparent;}#mermaid-svg-AHdwQGT6M4s7TyFP .label text,#mermaid-svg-AHdwQGT6M4s7TyFP span{fill:#333;color:#333;}#mermaid-svg-AHdwQGT6M4s7TyFP .node rect,#mermaid-svg-AHdwQGT6M4s7TyFP .node circle,#mermaid-svg-AHdwQGT6M4s7TyFP .node ellipse,#mermaid-svg-AHdwQGT6M4s7TyFP .node polygon,#mermaid-svg-AHdwQGT6M4s7TyFP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-AHdwQGT6M4s7TyFP .rough-node .label text,#mermaid-svg-AHdwQGT6M4s7TyFP .node .label text,#mermaid-svg-AHdwQGT6M4s7TyFP .image-shape .label,#mermaid-svg-AHdwQGT6M4s7TyFP .icon-shape .label{text-anchor:middle;}#mermaid-svg-AHdwQGT6M4s7TyFP .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-AHdwQGT6M4s7TyFP .rough-node .label,#mermaid-svg-AHdwQGT6M4s7TyFP .node .label,#mermaid-svg-AHdwQGT6M4s7TyFP .image-shape .label,#mermaid-svg-AHdwQGT6M4s7TyFP .icon-shape .label{text-align:center;}#mermaid-svg-AHdwQGT6M4s7TyFP .node.clickable{cursor:pointer;}#mermaid-svg-AHdwQGT6M4s7TyFP .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-AHdwQGT6M4s7TyFP .arrowheadPath{fill:#333333;}#mermaid-svg-AHdwQGT6M4s7TyFP .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-AHdwQGT6M4s7TyFP .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-AHdwQGT6M4s7TyFP .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AHdwQGT6M4s7TyFP .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-AHdwQGT6M4s7TyFP .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AHdwQGT6M4s7TyFP .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-AHdwQGT6M4s7TyFP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-AHdwQGT6M4s7TyFP .cluster text{fill:#333;}#mermaid-svg-AHdwQGT6M4s7TyFP .cluster span{color:#333;}#mermaid-svg-AHdwQGT6M4s7TyFP div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-AHdwQGT6M4s7TyFP .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-AHdwQGT6M4s7TyFP rect.text{fill:none;stroke-width:0;}#mermaid-svg-AHdwQGT6M4s7TyFP .icon-shape,#mermaid-svg-AHdwQGT6M4s7TyFP .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AHdwQGT6M4s7TyFP .icon-shape p,#mermaid-svg-AHdwQGT6M4s7TyFP .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-AHdwQGT6M4s7TyFP .icon-shape .label rect,#mermaid-svg-AHdwQGT6M4s7TyFP .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AHdwQGT6M4s7TyFP .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-AHdwQGT6M4s7TyFP .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-AHdwQGT6M4s7TyFP :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Native 代码异常
触发信号
系统生成 tombstone
用符号表还原堆栈
定位 so 和函数
修复越界 空指针 线程问题

Native Crash 排查里最容易踩两个坑:

  1. 没有保存符号表。线上堆栈只有地址,看起来像乱码。
  2. 只看 Java 调用点。真正崩溃可能在 native 内部线程,Java 层只是入口。

工程上建议:

做法 价值
每个版本保存符号表 线上 native 堆栈能还原函数名和行号
区分 ABI arm64-v8a、armeabi-v7a 堆栈不能混用
记录 so 版本 防止热更新或动态库版本不一致
收集 tombstone Android 12 以后可用 ApplicationExitInfo.getTraceInputStream() 读取相关 trace
对 native 入口做参数校验 少把脏数据传进 C/C++
对第三方 so 建立灰度和开关 出问题时能快速止血

Native Crash 的修复成本通常比 Java Crash 高,所以版本发布前更要重视灰度和符号管理。没有符号表,线上定位会很痛。

4. ANR:用户说"卡死了",系统说"你没响应"

ANR 是 Application Not Responding。它不是异常崩溃,而是系统发现你的应用在规定时间内没有响应关键事件。

官方文档列出的常见 ANR 类型包括:

ANR 类型 大白话 常见根因
Input dispatch timed out 用户点击或触摸后,主线程没及时处理 主线程长任务、锁等待、Binder 阻塞
Service timeout Service 生命周期或前台服务相关操作超时 onCreate() / onStartCommand() 太重
Broadcast timeout 广播接收处理超时 onReceive() 做 I/O 或同步等待
ContentProvider timeout Provider 发布或访问超时 Provider 初始化过重、启动期 I/O
No focused window 启动后没有及时创建可交互窗口 启动链路阻塞、首屏创建卡住

ANR 的核心问题通常是:主线程没有机会处理系统期待它处理的消息。
LockOrIO MainThread AndroidSystem User LockOrIO MainThread AndroidSystem User #mermaid-svg-KBnGXtGFzgT82Eqg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-KBnGXtGFzgT82Eqg .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-KBnGXtGFzgT82Eqg .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-KBnGXtGFzgT82Eqg .error-icon{fill:#552222;}#mermaid-svg-KBnGXtGFzgT82Eqg .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-KBnGXtGFzgT82Eqg .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-KBnGXtGFzgT82Eqg .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-KBnGXtGFzgT82Eqg .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-KBnGXtGFzgT82Eqg .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-KBnGXtGFzgT82Eqg .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-KBnGXtGFzgT82Eqg .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-KBnGXtGFzgT82Eqg .marker{fill:#333333;stroke:#333333;}#mermaid-svg-KBnGXtGFzgT82Eqg .marker.cross{stroke:#333333;}#mermaid-svg-KBnGXtGFzgT82Eqg svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-KBnGXtGFzgT82Eqg p{margin:0;}#mermaid-svg-KBnGXtGFzgT82Eqg .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-KBnGXtGFzgT82Eqg text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-KBnGXtGFzgT82Eqg .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-KBnGXtGFzgT82Eqg .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-KBnGXtGFzgT82Eqg .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-KBnGXtGFzgT82Eqg .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-KBnGXtGFzgT82Eqg #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-KBnGXtGFzgT82Eqg .sequenceNumber{fill:white;}#mermaid-svg-KBnGXtGFzgT82Eqg #sequencenumber{fill:#333;}#mermaid-svg-KBnGXtGFzgT82Eqg #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-KBnGXtGFzgT82Eqg .messageText{fill:#333;stroke:none;}#mermaid-svg-KBnGXtGFzgT82Eqg .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-KBnGXtGFzgT82Eqg .labelText,#mermaid-svg-KBnGXtGFzgT82Eqg .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-KBnGXtGFzgT82Eqg .loopText,#mermaid-svg-KBnGXtGFzgT82Eqg .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-KBnGXtGFzgT82Eqg .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-KBnGXtGFzgT82Eqg .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-KBnGXtGFzgT82Eqg .noteText,#mermaid-svg-KBnGXtGFzgT82Eqg .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-KBnGXtGFzgT82Eqg .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-KBnGXtGFzgT82Eqg .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-KBnGXtGFzgT82Eqg .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-KBnGXtGFzgT82Eqg .actorPopupMenu{position:absolute;}#mermaid-svg-KBnGXtGFzgT82Eqg .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-KBnGXtGFzgT82Eqg .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-KBnGXtGFzgT82Eqg .actor-man circle,#mermaid-svg-KBnGXtGFzgT82Eqg line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-KBnGXtGFzgT82Eqg :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} click or input 1 dispatch input event 2 wait lock or blocking IO 3 timeout check 4 ANR reported 5

ANR 排查不能只问"哪里卡了",要看主线程当时在等什么:

主线程栈表现 可能原因
Thread.sleep 直接睡主线程
Object.wait / LockSupport.park 等锁、等条件、等异步结果
BinderProxy.transact 等系统服务或跨进程返回
文件读写 主线程 I/O
SQLite 操作 主线程数据库
图片解码 主线程 CPU 或内存压力
MessageQueue.nativePollOnce 不一定是问题,要结合是否主线程空闲和其他线程状态

这里有个很常见的误会:看到主线程在 nativePollOnce,就以为没有卡。其实不一定。ANR trace 采样有时间差,如果抓栈时主线程已经恢复空闲,栈上可能就是 nativePollOnce。所以 ANR 要结合时间线、日志、Perfetto、其他线程锁持有者一起看。

5. ANR 优化:别让主线程"等答案"

ANR 的优化原则其实很朴素:主线程只做 UI 和很短的调度,不做重活,也不等重活。

5.1 不要在主线程做 I/O

错误示范:

kotlin 复制代码
fun onCreate() {
    val config = File(filesDir, "config.json").readText()
    render(config)
}

更稳的写法:

kotlin 复制代码
lifecycleScope.launch {
    val config = withContext(Dispatchers.IO) {
        File(filesDir, "config.json").readText()
    }
    render(config)
}

如果配置是首屏必须的,优先考虑更小的本地缓存、启动前置预热、异步骨架屏,而不是让主线程硬等。

5.2 不要在主线程等锁

比较隐蔽的 ANR 通常来自锁:

kotlin 复制代码
private val lock = Any()

fun updateOnMain() {
    synchronized(lock) {
        renderState()
    }
}

fun workInBackground() {
    synchronized(lock) {
        doSlowIO()
    }
}

后台线程拿着锁做慢 I/O,主线程进来等锁,用户一点击就可能 ANR。修复方向是缩小锁范围,不要在锁里做 I/O,不要让主线程等待后台长任务。

5.3 Broadcast 和 Service 要快进快出

广播的 onReceive() 不适合做长任务。Service 的生命周期方法也不适合塞重初始化。更推荐把耗时任务丢给 WorkManager、前台服务的后台线程、协程或专用线程池。

kotlin 复制代码
class BootReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        WorkManager.getInstance(context)
            .enqueue(buildSyncWork())
    }
}

这段代码只表达方向:广播里触发调度,真正耗时的工作交给后台执行。

6. OOM:不只是 Java 堆爆了

OOM 也要分情况。

类型 表现 常见原因
Java heap OOM 抛 OutOfMemoryError 大对象、集合失控、泄漏
Native OOM native 分配失败或崩溃 Bitmap、音视频、so 内存
Graphics 内存压力 渲染或图片相关异常 大图、纹理、硬件位图
LMK 杀进程 进程突然没了,没有 Java 堆栈 系统低内存回收

OOM 优化不要只盯 Java heap。Android 上 Bitmap、native buffer、graphics 内存、WebView、播放器、相机都可能把进程推到危险边缘。

常见治理点:

问题 优化方向
大图直接加载 按 View 尺寸解码,限制原图进入内存
Bitmap 缓存无上限 LRU、内存等级感知、页面退出清理
Activity 泄漏 LeakCanary、生命周期解绑、弱化回调持有
列表无限数据 分页、裁剪、Diff、避免全量常驻
WebView 内存高 独立进程、按需创建、页面销毁策略
native 内存泄漏 heapprofd、meminfo、native 分配点排查

#mermaid-svg-qHshWedIINxCrAPL{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-qHshWedIINxCrAPL .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-qHshWedIINxCrAPL .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-qHshWedIINxCrAPL .error-icon{fill:#552222;}#mermaid-svg-qHshWedIINxCrAPL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-qHshWedIINxCrAPL .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-qHshWedIINxCrAPL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-qHshWedIINxCrAPL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-qHshWedIINxCrAPL .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-qHshWedIINxCrAPL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-qHshWedIINxCrAPL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-qHshWedIINxCrAPL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-qHshWedIINxCrAPL .marker.cross{stroke:#333333;}#mermaid-svg-qHshWedIINxCrAPL svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-qHshWedIINxCrAPL p{margin:0;}#mermaid-svg-qHshWedIINxCrAPL .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-qHshWedIINxCrAPL .cluster-label text{fill:#333;}#mermaid-svg-qHshWedIINxCrAPL .cluster-label span{color:#333;}#mermaid-svg-qHshWedIINxCrAPL .cluster-label span p{background-color:transparent;}#mermaid-svg-qHshWedIINxCrAPL .label text,#mermaid-svg-qHshWedIINxCrAPL span{fill:#333;color:#333;}#mermaid-svg-qHshWedIINxCrAPL .node rect,#mermaid-svg-qHshWedIINxCrAPL .node circle,#mermaid-svg-qHshWedIINxCrAPL .node ellipse,#mermaid-svg-qHshWedIINxCrAPL .node polygon,#mermaid-svg-qHshWedIINxCrAPL .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-qHshWedIINxCrAPL .rough-node .label text,#mermaid-svg-qHshWedIINxCrAPL .node .label text,#mermaid-svg-qHshWedIINxCrAPL .image-shape .label,#mermaid-svg-qHshWedIINxCrAPL .icon-shape .label{text-anchor:middle;}#mermaid-svg-qHshWedIINxCrAPL .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-qHshWedIINxCrAPL .rough-node .label,#mermaid-svg-qHshWedIINxCrAPL .node .label,#mermaid-svg-qHshWedIINxCrAPL .image-shape .label,#mermaid-svg-qHshWedIINxCrAPL .icon-shape .label{text-align:center;}#mermaid-svg-qHshWedIINxCrAPL .node.clickable{cursor:pointer;}#mermaid-svg-qHshWedIINxCrAPL .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-qHshWedIINxCrAPL .arrowheadPath{fill:#333333;}#mermaid-svg-qHshWedIINxCrAPL .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-qHshWedIINxCrAPL .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-qHshWedIINxCrAPL .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qHshWedIINxCrAPL .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-qHshWedIINxCrAPL .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qHshWedIINxCrAPL .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-qHshWedIINxCrAPL .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-qHshWedIINxCrAPL .cluster text{fill:#333;}#mermaid-svg-qHshWedIINxCrAPL .cluster span{color:#333;}#mermaid-svg-qHshWedIINxCrAPL div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-qHshWedIINxCrAPL .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-qHshWedIINxCrAPL rect.text{fill:none;stroke-width:0;}#mermaid-svg-qHshWedIINxCrAPL .icon-shape,#mermaid-svg-qHshWedIINxCrAPL .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qHshWedIINxCrAPL .icon-shape p,#mermaid-svg-qHshWedIINxCrAPL .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-qHshWedIINxCrAPL .icon-shape .label rect,#mermaid-svg-qHshWedIINxCrAPL .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qHshWedIINxCrAPL .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-qHshWedIINxCrAPL .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-qHshWedIINxCrAPL :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 内存压力上升
Java heap
Native heap
Graphics
LMK 杀进程
对象泄漏
大对象
Native 分配
图片和纹理
ApplicationExitInfo

如果线上看到"进程消失但没有 Crash 堆栈",不要急着说平台漏采。它可能是低内存杀进程、用户手动杀、系统限制、后台限制。Android 11 以后可以通过 ApplicationExitInfo 查询历史退出原因,这对分析 ANR、低内存、Native Crash 等情况很有用。

7. 线上采集:报告要能还原现场

一个稳定性平台,最重要的不是"能收到异常",而是能不能帮助你还原现场。

我会把采集分成四层:
#mermaid-svg-zmWTvnsYhLzTbS5M{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-zmWTvnsYhLzTbS5M .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-zmWTvnsYhLzTbS5M .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-zmWTvnsYhLzTbS5M .error-icon{fill:#552222;}#mermaid-svg-zmWTvnsYhLzTbS5M .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-zmWTvnsYhLzTbS5M .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-zmWTvnsYhLzTbS5M .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-zmWTvnsYhLzTbS5M .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-zmWTvnsYhLzTbS5M .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-zmWTvnsYhLzTbS5M .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-zmWTvnsYhLzTbS5M .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-zmWTvnsYhLzTbS5M .marker{fill:#333333;stroke:#333333;}#mermaid-svg-zmWTvnsYhLzTbS5M .marker.cross{stroke:#333333;}#mermaid-svg-zmWTvnsYhLzTbS5M svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-zmWTvnsYhLzTbS5M p{margin:0;}#mermaid-svg-zmWTvnsYhLzTbS5M .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-zmWTvnsYhLzTbS5M .cluster-label text{fill:#333;}#mermaid-svg-zmWTvnsYhLzTbS5M .cluster-label span{color:#333;}#mermaid-svg-zmWTvnsYhLzTbS5M .cluster-label span p{background-color:transparent;}#mermaid-svg-zmWTvnsYhLzTbS5M .label text,#mermaid-svg-zmWTvnsYhLzTbS5M span{fill:#333;color:#333;}#mermaid-svg-zmWTvnsYhLzTbS5M .node rect,#mermaid-svg-zmWTvnsYhLzTbS5M .node circle,#mermaid-svg-zmWTvnsYhLzTbS5M .node ellipse,#mermaid-svg-zmWTvnsYhLzTbS5M .node polygon,#mermaid-svg-zmWTvnsYhLzTbS5M .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zmWTvnsYhLzTbS5M .rough-node .label text,#mermaid-svg-zmWTvnsYhLzTbS5M .node .label text,#mermaid-svg-zmWTvnsYhLzTbS5M .image-shape .label,#mermaid-svg-zmWTvnsYhLzTbS5M .icon-shape .label{text-anchor:middle;}#mermaid-svg-zmWTvnsYhLzTbS5M .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-zmWTvnsYhLzTbS5M .rough-node .label,#mermaid-svg-zmWTvnsYhLzTbS5M .node .label,#mermaid-svg-zmWTvnsYhLzTbS5M .image-shape .label,#mermaid-svg-zmWTvnsYhLzTbS5M .icon-shape .label{text-align:center;}#mermaid-svg-zmWTvnsYhLzTbS5M .node.clickable{cursor:pointer;}#mermaid-svg-zmWTvnsYhLzTbS5M .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-zmWTvnsYhLzTbS5M .arrowheadPath{fill:#333333;}#mermaid-svg-zmWTvnsYhLzTbS5M .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-zmWTvnsYhLzTbS5M .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-zmWTvnsYhLzTbS5M .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zmWTvnsYhLzTbS5M .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-zmWTvnsYhLzTbS5M .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zmWTvnsYhLzTbS5M .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-zmWTvnsYhLzTbS5M .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-zmWTvnsYhLzTbS5M .cluster text{fill:#333;}#mermaid-svg-zmWTvnsYhLzTbS5M .cluster span{color:#333;}#mermaid-svg-zmWTvnsYhLzTbS5M div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-zmWTvnsYhLzTbS5M .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-zmWTvnsYhLzTbS5M rect.text{fill:none;stroke-width:0;}#mermaid-svg-zmWTvnsYhLzTbS5M .icon-shape,#mermaid-svg-zmWTvnsYhLzTbS5M .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zmWTvnsYhLzTbS5M .icon-shape p,#mermaid-svg-zmWTvnsYhLzTbS5M .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-zmWTvnsYhLzTbS5M .icon-shape .label rect,#mermaid-svg-zmWTvnsYhLzTbS5M .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zmWTvnsYhLzTbS5M .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-zmWTvnsYhLzTbS5M .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-zmWTvnsYhLzTbS5M :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 异常或卡死事件
运行时信息
设备和系统信息
业务现场
时间线
稳定性报告
聚合归因
修复和验证

具体字段可以按这个表设计:

维度 建议采集
运行时 线程堆栈、异常类型、ANR trace、native tombstone、内存快照摘要
设备 系统版本、机型、ABI、内存等级、屏幕、厂商 ROM
App 版本、构建号、渠道、灰度组、混淆映射版本
页面 当前 Activity、Fragment、路由、Tab、前后台状态
用户动作 最近点击、接口请求、页面跳转、关键埋点
资源 内存占用、线程数、文件句柄、磁盘空间
网络 网络类型、请求错误、超时、服务端状态码
发布 是否热修、是否动态配置、是否实验开关命中

采集越多越好吗?也不是。要注意隐私、体积和性能成本。稳定性日志最好记录"定位必要的信息",不要把用户敏感数据和完整业务内容无脑上传。

8. 治理闭环:从发现到止血,再到防复发

稳定性问题要按事故流程处理,而不是靠个人英雄主义。
#mermaid-svg-ChtCr43Lb0JFscnO{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ChtCr43Lb0JFscnO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ChtCr43Lb0JFscnO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ChtCr43Lb0JFscnO .error-icon{fill:#552222;}#mermaid-svg-ChtCr43Lb0JFscnO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ChtCr43Lb0JFscnO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ChtCr43Lb0JFscnO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ChtCr43Lb0JFscnO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ChtCr43Lb0JFscnO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ChtCr43Lb0JFscnO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ChtCr43Lb0JFscnO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ChtCr43Lb0JFscnO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ChtCr43Lb0JFscnO .marker.cross{stroke:#333333;}#mermaid-svg-ChtCr43Lb0JFscnO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ChtCr43Lb0JFscnO p{margin:0;}#mermaid-svg-ChtCr43Lb0JFscnO .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ChtCr43Lb0JFscnO .cluster-label text{fill:#333;}#mermaid-svg-ChtCr43Lb0JFscnO .cluster-label span{color:#333;}#mermaid-svg-ChtCr43Lb0JFscnO .cluster-label span p{background-color:transparent;}#mermaid-svg-ChtCr43Lb0JFscnO .label text,#mermaid-svg-ChtCr43Lb0JFscnO span{fill:#333;color:#333;}#mermaid-svg-ChtCr43Lb0JFscnO .node rect,#mermaid-svg-ChtCr43Lb0JFscnO .node circle,#mermaid-svg-ChtCr43Lb0JFscnO .node ellipse,#mermaid-svg-ChtCr43Lb0JFscnO .node polygon,#mermaid-svg-ChtCr43Lb0JFscnO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ChtCr43Lb0JFscnO .rough-node .label text,#mermaid-svg-ChtCr43Lb0JFscnO .node .label text,#mermaid-svg-ChtCr43Lb0JFscnO .image-shape .label,#mermaid-svg-ChtCr43Lb0JFscnO .icon-shape .label{text-anchor:middle;}#mermaid-svg-ChtCr43Lb0JFscnO .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ChtCr43Lb0JFscnO .rough-node .label,#mermaid-svg-ChtCr43Lb0JFscnO .node .label,#mermaid-svg-ChtCr43Lb0JFscnO .image-shape .label,#mermaid-svg-ChtCr43Lb0JFscnO .icon-shape .label{text-align:center;}#mermaid-svg-ChtCr43Lb0JFscnO .node.clickable{cursor:pointer;}#mermaid-svg-ChtCr43Lb0JFscnO .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ChtCr43Lb0JFscnO .arrowheadPath{fill:#333333;}#mermaid-svg-ChtCr43Lb0JFscnO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ChtCr43Lb0JFscnO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ChtCr43Lb0JFscnO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ChtCr43Lb0JFscnO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ChtCr43Lb0JFscnO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ChtCr43Lb0JFscnO .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ChtCr43Lb0JFscnO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ChtCr43Lb0JFscnO .cluster text{fill:#333;}#mermaid-svg-ChtCr43Lb0JFscnO .cluster span{color:#333;}#mermaid-svg-ChtCr43Lb0JFscnO div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ChtCr43Lb0JFscnO .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ChtCr43Lb0JFscnO rect.text{fill:none;stroke-width:0;}#mermaid-svg-ChtCr43Lb0JFscnO .icon-shape,#mermaid-svg-ChtCr43Lb0JFscnO .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ChtCr43Lb0JFscnO .icon-shape p,#mermaid-svg-ChtCr43Lb0JFscnO .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ChtCr43Lb0JFscnO .icon-shape .label rect,#mermaid-svg-ChtCr43Lb0JFscnO .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ChtCr43Lb0JFscnO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ChtCr43Lb0JFscnO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ChtCr43Lb0JFscnO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 发现指标异常
分级和归因
止血
修复根因
灰度验证
扩大发布
复盘和防回归
门禁和监控

每一步都有不同重点:

阶段 目标 常见动作
发现 知道出问题 告警、看板、版本对比
分级 判断严重程度 影响用户数、是否启动路径、是否支付路径
止血 降低损伤 关闭开关、回滚配置、暂停灰度、下架版本
修复 解决根因 代码修复、兼容、补测试
验证 确认有效 灰度、复现用例、指标回落
复盘 防止再犯 增加门禁、监控、规范、owner

止血能力特别重要。线上大事故时,最怕只有"发新版修复"一条路。稳定性好的 App 通常会准备:

能力 用处
配置开关 快速关闭高风险功能
灰度发布 限制影响范围
服务端降级 接口异常时给默认值或兜底页
客户端降级 页面模块加载失败时不拖垮整页
版本回滚 发布平台允许时快速回退
热修能力 谨慎用于明确且低风险的问题

但别滥用热修。热修适合小范围、明确原因、风险可控的问题。线程模型、内存生命周期、复杂状态机这类问题,热修有时会把坑补成另一个坑。

9. 预防比修复更便宜

稳定性优化不该只发生在线上事故后。开发阶段就能做很多事。

9.1 编码层防线

场景 建议
可空数据 Kotlin 可空类型、入口校验、默认值
状态流转 用明确状态机,不让任意状态调用任意方法
生命周期 协程作用域、取消回调、页面销毁不更新 UI
线程 主线程只做 UI,重活切后台
集合访问 防止异步刷新后下标过期
外部输入 服务端字段、Intent 参数、DeepLink 参数都要校验

9.2 工程层防线

防线 作用
Lint 和静态扫描 提前发现主线程 I/O、危险 API、空指针风险
单元测试 覆盖状态机、边界输入、解析兼容
UI 自动化 覆盖核心路径和启动路径
Monkey / 稳定性测试 找随机交互下的崩溃和 ANR
内存专项 找泄漏、图片内存、缓存失控
性能专项 找卡顿、启动慢、主线程长任务
发布门禁 Crash/ANR 指标异常时阻止扩大灰度

9.3 依赖治理

很多稳定性问题来自 SDK:

  • 广告 SDK 在启动期初始化过重。
  • 地图 SDK 在低端机 native crash。
  • 推送 SDK 多进程初始化不区分进程。
  • 图片库配置不当导致 OOM。
  • WebView 容器持有 Activity 泄漏。

三方 SDK 要有 owner,有版本记录,有灰度,有开关。不要让它们在 Application.onCreate() 里随便跑,更不要让 SDK 异常直接拖垮主流程。

10. ANR 与 Crash 的排查顺序

线上来了一个稳定性问题,可以按这个顺序走:
#mermaid-svg-Qo1idEAPlLXEajoC{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Qo1idEAPlLXEajoC .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Qo1idEAPlLXEajoC .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Qo1idEAPlLXEajoC .error-icon{fill:#552222;}#mermaid-svg-Qo1idEAPlLXEajoC .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Qo1idEAPlLXEajoC .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Qo1idEAPlLXEajoC .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Qo1idEAPlLXEajoC .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Qo1idEAPlLXEajoC .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Qo1idEAPlLXEajoC .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Qo1idEAPlLXEajoC .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Qo1idEAPlLXEajoC .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Qo1idEAPlLXEajoC .marker.cross{stroke:#333333;}#mermaid-svg-Qo1idEAPlLXEajoC svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Qo1idEAPlLXEajoC p{margin:0;}#mermaid-svg-Qo1idEAPlLXEajoC .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Qo1idEAPlLXEajoC .cluster-label text{fill:#333;}#mermaid-svg-Qo1idEAPlLXEajoC .cluster-label span{color:#333;}#mermaid-svg-Qo1idEAPlLXEajoC .cluster-label span p{background-color:transparent;}#mermaid-svg-Qo1idEAPlLXEajoC .label text,#mermaid-svg-Qo1idEAPlLXEajoC span{fill:#333;color:#333;}#mermaid-svg-Qo1idEAPlLXEajoC .node rect,#mermaid-svg-Qo1idEAPlLXEajoC .node circle,#mermaid-svg-Qo1idEAPlLXEajoC .node ellipse,#mermaid-svg-Qo1idEAPlLXEajoC .node polygon,#mermaid-svg-Qo1idEAPlLXEajoC .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Qo1idEAPlLXEajoC .rough-node .label text,#mermaid-svg-Qo1idEAPlLXEajoC .node .label text,#mermaid-svg-Qo1idEAPlLXEajoC .image-shape .label,#mermaid-svg-Qo1idEAPlLXEajoC .icon-shape .label{text-anchor:middle;}#mermaid-svg-Qo1idEAPlLXEajoC .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Qo1idEAPlLXEajoC .rough-node .label,#mermaid-svg-Qo1idEAPlLXEajoC .node .label,#mermaid-svg-Qo1idEAPlLXEajoC .image-shape .label,#mermaid-svg-Qo1idEAPlLXEajoC .icon-shape .label{text-align:center;}#mermaid-svg-Qo1idEAPlLXEajoC .node.clickable{cursor:pointer;}#mermaid-svg-Qo1idEAPlLXEajoC .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Qo1idEAPlLXEajoC .arrowheadPath{fill:#333333;}#mermaid-svg-Qo1idEAPlLXEajoC .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Qo1idEAPlLXEajoC .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Qo1idEAPlLXEajoC .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Qo1idEAPlLXEajoC .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Qo1idEAPlLXEajoC .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Qo1idEAPlLXEajoC .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Qo1idEAPlLXEajoC .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Qo1idEAPlLXEajoC .cluster text{fill:#333;}#mermaid-svg-Qo1idEAPlLXEajoC .cluster span{color:#333;}#mermaid-svg-Qo1idEAPlLXEajoC div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Qo1idEAPlLXEajoC .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Qo1idEAPlLXEajoC rect.text{fill:none;stroke-width:0;}#mermaid-svg-Qo1idEAPlLXEajoC .icon-shape,#mermaid-svg-Qo1idEAPlLXEajoC .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Qo1idEAPlLXEajoC .icon-shape p,#mermaid-svg-Qo1idEAPlLXEajoC .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Qo1idEAPlLXEajoC .icon-shape .label rect,#mermaid-svg-Qo1idEAPlLXEajoC .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Qo1idEAPlLXEajoC .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Qo1idEAPlLXEajoC .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Qo1idEAPlLXEajoC :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Java Crash
Native Crash
ANR
OOM
稳定性问题
先分类
看异常类型和主堆栈
看 signal 和 tombstone
看主线程栈和等待链
看内存曲线和对象持有
看影响版本和用户
构造复现场景
修复和灰度验证

更细一点:

问题 第一眼看什么 第二步看什么
Java Crash 异常类型、顶部业务堆栈 页面、参数、版本差异
Native Crash signal、崩溃线程、so 名称 符号化、ABI、设备集中度
ANR 主线程栈 锁持有者、Binder 对端、I/O、Perfetto
OOM OOM 类型、内存区域 图片、缓存、泄漏、native 分配
启动失败 是否 Provider / Application / 首 Activity 启动链路埋点和灰度配置

一个小技巧:不要只看 Top 1。Top 1 可能是兜底异常,比如 IllegalStateException,真正根因藏在更早的业务日志里。稳定性平台最好能看"崩溃前 30 秒发生了什么"。

11. 面试怎么讲崩溃优化和稳定性

面试里如果问"你做过崩溃优化吗",不要只说"接了 Crash 平台,修了空指针"。可以这样讲:

text 复制代码
我会先把稳定性问题分类,不会只看 Crash。
Crash 里又分 Java Crash 和 Native Crash;ANR 是主线程或关键组件超时没有响应;OOM 还要区分 Java heap、native 内存、graphics 内存和低内存杀进程。

采集上,Java Crash 可以通过 UncaughtExceptionHandler 记录异常、线程、设备、版本、页面和业务现场;Native Crash 要保留 tombstone 和符号表;ANR 要看 traces、主线程堆栈、锁等待、Binder 和 I/O;Android 11 以后还可以通过 ApplicationExitInfo 辅助分析历史退出原因。

优化上,我不会靠全局 try-catch 硬吞异常,而是按根因治理:空指针和状态异常靠数据校验和状态机;ANR 靠主线程治理、I/O 下沉、锁范围收敛;OOM 靠图片内存、缓存、泄漏和 native 内存治理;线上再配合灰度、开关、降级、回滚和告警。

指标上,我会看 Crash 率、ANR 率、启动失败率、影响用户数、Top 问题占比和新版本新增问题。最终要形成发现、止血、修复、验证、防回归的闭环。

如果面试官继续问"ANR 怎么排查",可以接:

text 复制代码
我会先看 ANR 类型,是 input、broadcast、service 还是 provider。
然后看主线程栈,判断是在执行耗时任务、等锁、Binder 调用、磁盘 I/O,还是采样时已经回到 nativePollOnce。
如果是锁等待,要找持锁线程;如果是 Binder,要看对端服务;如果是启动期 ANR,要重点查 ContentProvider、Application 和首屏初始化。
最后用 Perfetto 或线上 trace 还原时间线,而不是只凭一段主线程堆栈下结论。

这套回答的关键是:你把稳定性当系统工程,而不是把崩溃当零散 bug。

12. 小结

稳定性治理可以压成一句话:

text 复制代码
先分类,再采集;先止血,再修根因;最后把经验变成门禁和监控。

Crash、ANR、OOM、Native Crash 的排查路径不同,但它们最后都会回到同一套工程能力:现场要采得到,问题要分得清,影响面要控得住,修复要验证,复发要被门禁拦住。

所以别把崩溃优化理解成"少崩几次"。真正的目标是让 App 在真实设备、真实网络、真实用户行为下,遇到异常也能尽量降级,出了问题能快速定位,发版之后能被指标守住。

13. 源码与参考入口

以下入口按 2026-10-09 查询和整理,用来辅助定位官方口径;具体实现请以项目目标 Android 版本、Google Play Console 当前规则和接入平台为准。

主题 链接
Android Vitals 总览 Monitor your app's technical quality with Android vitals
Crash bad behavior 阈值 Crash rate
ANR bad behavior 阈值 ANR rate
ANR 诊断与修复 Diagnose and fix ANRs
ApplicationExitInfo ApplicationExitInfo reference
Tombstone 和 native crash 信息 ApplicationExitInfo API
Android 性能测量总览 Overview of measuring app performance
相关推荐
ZHOUPUYU1 小时前
PHP 高级技术实战:从原理到高并发架构落地
android·性能优化·php
stevenzqzq2 小时前
mvi的理解
android·compose
脚踏实地,坚持不懈!2 小时前
Android 电池健康度检测全流程(驱动 → 内核 → HAL → Framework)
android·linux
恋猫de小郭2 小时前
Genkit Dart 1.0 发布,Flutter 原生的 AI Agent 终于完整了
android·前端·flutter
迪康妍妍2 小时前
终端安全实战:用迪康终端安全管理系统实现U盘四分档管控与全量审计
android·运维·网络·安全·电脑
JackLam2 小时前
mobile-use 使用与接入第三方 LLM 网关完整指南OC
android·ai·mobile use
snotJam2 小时前
关于Uniapp的Android自定义基座使用
android·uni-app
TST-魚爷3 小时前
易视讯会议摄像头电视安装教程
android·电脑·钉钉·腾讯会议·zoom会议
墨天梦12 小时前
B17_LifecycleLiveData与Flow
android·kotlin