为什么你需要懂 Crash 捕获与解析

做 Android 开发这些年,我遇到最头疼的问题就是——App 上线后突然闪退,用户骂声一片,但 log 里只留下一行「signal 11 (SIGSEGV)」。你根本不知道哪里挂了。更崩溃的是,Java 层的堆栈清晰得很,Native 层却一片空白。

说白了,Java 和 Native 的 Crash 是两套完全不同的东西。Java 崩溃靠虚拟机托管,异常能抛到顶层。Native 崩溃呢?直接 SIGKILL,连个招呼都不打。你想想看,如果你的 App 用了 NDK、WebRTC、或者第三方 so 库,一旦 Native 挂了,连堆栈都捞不到,那调试起来就像在黑暗里找一根针。

我个人习惯是,先搞清楚 Crash 是怎么发生的,再谈怎么抓、怎么解。所以这节课,咱们就从最基础的概念说起。

Crash 的本质:两种死亡方式

Java Crash,本质是未捕获的异常。比如 NullPointerException、OutOfMemoryError。这些异常如果没有被 try-catch 处理,就会沿着调用栈往上抛,最终被 Thread.UncaughtExceptionHandler 抓住,然后 App 就挂了。

Native Crash 更粗暴。它是操作系统层面的信号。比如访问了非法内存地址(SIGSEGV)、除以零(SIGFPE)、或者调用了一个不存在的函数(SIGILL)。这些信号一旦产生,内核直接向进程发信号,进程默认动作就是终止。

我在项目中遇到过,有个 so 库在 Release 包上总是莫名其妙的闪退,但 Debug 包怎么跑都没事。排查半天,原来是编译器优化把空指针检查给优化掉了……嗯,这种坑,你光看 Java 堆栈是根本看不出来的。

课程到底讲什么

这门课会带你走完完整的路径:从捕获到解析,再到修复。我们覆盖三个核心环节:

  • 捕获层:Java 层用 Thread.UncaughtExceptionHandler,Native 层用 signal/seh 处理机制。我会教你如何同时注册两套 handler,确保任何崩溃都不会漏掉。
  • 收集层:拿到原始堆栈还不够,要收集线程状态、内存快照、CPU 寄存器值。这些信息才是定位问题的钥匙。
  • 解析层:把 Native 的地址偏移量通过符号表(.sym / .dSYM)翻译成函数名和行号。Java 则通过混淆映射表还原。

一句话总结:学完这门课,你就能从零搭建一套自己的 Crash 采集和符号化系统,不再依赖第三方平台。

为了让你一目了然,我把这 30 章的核心知识结构画成了图。你看一遍,心里就有谱了。

Crash 捕获与解析知识体系 安卓 Crash 处理 Java Crash Native Crash UncaughtExceptionHandler 异常堆栈收集 混淆映射还原 Signal Handler 地址 / 寄存器收集 符号化(addr2line) 统一解析引擎 底层信号处理 → 堆栈收集 → 符号还原,三层缺一不可

课程学习目标

学完这 30 章,你会具备以下能力:

  1. 独立实现 Crash 捕获:Java 层和 Native 层的 handler 能写能注册,不依赖第三方库。
  2. 读懂原始 Crash 信息:能区分 SIGSEGV 和 SIGABRT,能看懂 /data/tombstones 里的寄存器值和调用栈。
  3. 搭建符号化工具链:用 addr2line、llvm-symbolizer 或者自己写脚本把地址转成代码行号。
  4. 处理常见痛点:如混淆映射、so 库 strip 后无法符号化、多线程 Crash 下的上下文丢失。

我的建议:不要跳过 Native 部分。很多同学觉得 Java 不就行了?但你想想,现在哪个 App 不用 Flutter、React Native、或者自己写的 NDK 代码?Native 崩溃一旦出现,没有工具链就是死路一条。

谁适合学这门课

这份课程不是给小白看的。你需要满足以下前置条件:

  • 有 2 年以上 Android 开发经验,能写简单的 C/C++ 代码。
  • 知道 JNI 的基本调用方式。
  • 会用 Android Studio 和 NDK 编译 so 库。
  • 最好遇到过几次闪退,但是不知道怎么 debug。
角色你会学到什么
应用层开发Java Crash 的全面捕获 + 混淆映射处理
SDK / 底层开发Native Signal 注册 + 堆栈 unwind 原理
测试 / 运维如何用 tombstone 和 logcat 快速定位问题

课程整体安排

我把 30 章分成四个阶段,每个阶段都有一个可交付的成果:

  • 阶段一(第1-7章):搭建基础 Crash 捕获框架,Java 和 Native 双通道。
  • 阶段二(第8-15章):深入信号处理机制,搞懂 unwind 库(libunwind、libcorkscrew)。
  • 阶段三(第16-22章):符号化与混淆还原,拿实际 Release 包练手。
  • 阶段四(第23-30章):性能优化与生产环境部署,附真实案例复盘。

注意别踩的坑:千万不要在 Crash Handler 里做冗长的 IO 操作!我曾经在 Signal Handler 里直接写文件,结果再次触发 SIGSEGV 导致死循环——手机直接重启了。正确的做法是只保存最小上下文,把写文件的逻辑放到另一个进程去做。

第一个小练习:试试你的环境

既然要学 Crash 捕获,先确保你的开发环境能正常编译运行一个带 NDK 的项目。如果你还没配置好,按下面步骤来:

  1. 下载 NDK r25 或更新版本。
  2. 创建一个新的 Native C++ 项目。
  3. 在 native-lib.cpp 里故意写一个空指针解引用:
// 制造一个 Native Crash
int* p = nullptr;
*p = 42;  // 这里会触发 SIGSEGV
  1. 运行 App,观察 logcat 输出:你会看到 “signal 11 (SIGSEGV), fault addr 0x00000000” 之类的信息。嗯,这就是我们要抓住的东西。