rokevin
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
  • Crash

  • Android Crash 的分类与常见原因
    • 1. Java/Kotlin 层 Crash
    • 2. Native 层 Crash
    • 3. ANR(应用无响应)
  • Android Crash 的监控方案
    • 1. 自研监控方案
      • (1)Java 层 Crash 监控:UncaughtExceptionHandler
      • (2)Native 层 Crash 监控
      • (3)ANR 监控
    • 2. 第三方监控平台
  • Android Crash 的分析流程
    • 1. 收集崩溃上下文信息
    • 2. 定位崩溃根因
      • (1)Java 层 Crash 分析
      • (2)Native 层 Crash 分析
      • (3)ANR 分析
    • 3. 复现与验证
  • Android Crash 的预防措施
  • 关键工具推荐
  • Android Crash 排查速查清单
    • 前期准备:必备信息与工具
    • 分类型排查步骤
      • 1. Java/Kotlin 层 Crash(最高频)
      • 2. Native 层 Crash(难度高)
      • 3. ANR(应用无响应)
    • 通用修复与预防措施
    • 紧急排查技巧(线上崩溃)

Crash

Android Crash 指的是应用在运行过程中发生未捕获异常或致命错误,导致进程被系统终止的现象。它会直接影响用户体验,甚至造成用户流失,是 Android 开发和运维中必须重点解决的问题。

Android Crash 的分类与常见原因

根据崩溃的触发层级和原因,可分为 Java/Kotlin 层 Crash、Native 层 Crash 和 ANR(Application Not Responding) 三类,其中 ANR 虽不属于严格意义的 Crash,但表现和影响与 Crash 类似。

1. Java/Kotlin 层 Crash

由 Java/Kotlin 代码的未捕获异常引发,可通过 try-catch 捕获,也是最常见的崩溃类型。

  • 常见原因
    1. 空指针异常(NullPointerException):调用了 null 对象的方法或属性(最高频原因)。
    2. 数组越界(ArrayIndexOutOfBoundsException):访问数组下标超出范围。
    3. 类型转换异常(ClassCastException):强制类型转换失败(如 String 转 TextView)。
    4. 资源未找到(ResourceNotFoundException):引用了不存在的资源 ID(如布局、图片)。
    5. 并发异常:如 ConcurrentModificationException(迭代集合时修改元素)。
    6. 权限异常:未申请权限就调用相关 API(如 Android 6.0+ 危险权限)。

2. Native 层 Crash

由 C/C++ 代码引发的崩溃,通常是 JNI 调用错误或底层库问题,堆栈信息复杂,定位难度高。

  • 常见原因
    1. JNI 调用时类型不匹配(如 Java int 与 Native long 混用)。
    2. 访问非法内存地址(如空指针、野指针)。
    3. 底层库(如 so 库)版本不兼容或编译错误。
    4. 内存泄漏导致内存耗尽(OOM)。

3. ANR(应用无响应)

ANR :应用主线程(UI 线程)阻塞超过阈值(输入事件 5s,广播 10s,服务 20s),系统弹出 ANR 对话框。

  • 常见原因
    1. 主线程执行耗时操作(如网络请求、数据库读写、复杂计算)。
    2. 主线程被死锁阻塞。
    3. 大量耗时任务抢占主线程资源(如频繁的 Handler 消息发送)。

Android Crash 的监控方案

监控的核心目标是 捕获崩溃信息、上报到服务端、统计崩溃率,常用方案分为 自研监控 和 第三方平台 两类。

1. 自研监控方案

(1)Java 层 Crash 监控:UncaughtExceptionHandler

Android 提供了 Thread.UncaughtExceptionHandler 接口,可全局捕获未处理的 Java 异常。

  • 核心原理
    1. 实现 UncaughtExceptionHandler 接口,重写 uncaughtException 方法,在方法中收集崩溃信息。
    2. 通过 Thread.setDefaultUncaughtExceptionHandler 设置全局异常处理器。
  • 关键信息收集
    • 崩溃堆栈(Throwable.getStackTrace())
    • 设备信息(机型、系统版本、SDK 版本)
    • 应用信息(版本号、包名)
    • 用户行为(如崩溃前的操作步骤)
  • 代码示例
public class CrashHandler implements Thread.UncaughtExceptionHandler {
    private static CrashHandler instance = new CrashHandler();
    private Thread.UncaughtExceptionHandler mDefaultHandler;

    private CrashHandler() {
        // 获取系统默认的异常处理器
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
    }

    public static CrashHandler getInstance() {
        return instance;
    }

    public void init() {
        // 设置当前类为全局异常处理器
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        // 1. 收集崩溃信息
        collectCrashInfo(e);
        // 2. 保存到本地文件(防止上报失败)
        saveCrashInfoToFile(e);
        // 3. 上报到服务端
        uploadCrashInfo();
        // 4. 交给系统默认处理器(可选,会弹出崩溃对话框)
        if (mDefaultHandler != null) {
            mDefaultHandler.uncaughtException(t, e);
        }
    }

    private void collectCrashInfo(Throwable e) {
        // 收集设备、应用、堆栈信息
    }

    private void saveCrashInfoToFile(Throwable e) {
        // 保存到本地 SD 卡或内部存储
    }

    private void uploadCrashInfo() {
        // 上传到后端服务器
    }
}

(2)Native 层 Crash 监控

Native 层崩溃无法通过 Java 层的异常处理器捕获,需借助 信号量机制 和 第三方库。

  • 核心原理:Linux 系统中,Native 崩溃会触发信号(如 SIGSEGV 段错误、SIGABRT 中止信号),通过注册信号处理器捕获崩溃信息。
  • 常用工具
    1. Breakpad:Google 开源的跨平台崩溃捕获库,可生成 minidump 崩溃文件,结合符号表解析堆栈。
    2. Android NDK 的 ndk-stack 工具:用于解析 Native 崩溃的堆栈日志(需要 so 库的符号表)。

(3)ANR 监控

ANR 的监控比普通 Crash 复杂,核心是监控主线程阻塞状态。

  • 方案 1:利用系统日志

    系统发生 ANR 时,会在 /data/anr/traces.txt文件中写入详细日志(主线程堆栈、CPU 使用率等)。可通过定时读取该文件,检测 ANR 并上报。

    • 缺点:需要 root 权限,非 root 设备无法直接读取。
  • 方案 2:自研监控(主线程 WatchDog)

    启动一个独立的监控线程,定期向主线程发送心跳消息,如果主线程在指定时间内未响应,则判定为 ANR。

    • 核心类:Handler + 子线程定时发送消息。

2. 第三方监控平台

自研监控成本高,中小型应用推荐使用成熟的第三方平台,功能更全面且无需维护。

平台核心优势支持的崩溃类型
Firebase CrashlyticsGoogle 官方出品,免费,集成简单,支持实时上报Java/Kotlin、Native、ANR
Bugly(腾讯)国内常用,支持多平台,提供崩溃分析、用户行为回溯Java/Kotlin、Native、ANR
友盟 U-App结合用户画像,支持崩溃归因,适合运营分析Java/Kotlin、Native
Sentry开源可私有化部署,支持跨端(Android、iOS、Web),实时告警全类型崩溃

Android Crash 的分析流程

捕获到崩溃信息后,需要定位根因并修复问题,通用分析流程如下:

1. 收集崩溃上下文信息

分析前必须获取完整的信息,否则难以定位问题:

  • 基础信息:崩溃堆栈、设备型号、系统版本、应用版本、崩溃时间。
  • 上下文信息:用户操作步骤、网络状态(Wi-Fi/4G)、是否低内存、是否有其他应用干扰。
  • 扩展信息:ANR 需结合 CPU 使用率、主线程堆栈;Native 崩溃需结合 so 库版本和符号表。

2. 定位崩溃根因

(1)Java 层 Crash 分析

直接查看崩溃堆栈,最顶层的堆栈行就是崩溃发生的代码位置。

  • 示例:空指针异常堆栈
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
    at com.example.myapp.MainActivity.onCreate(MainActivity.java:20)
    at android.app.Activity.performCreate(Activity.java:8198)
  • 定位:第 20 行的 TextView 对象为 null,未初始化就调用 setText。

(2)Native 层 Crash 分析

Native 崩溃的堆栈是内存地址,需要通过符号表将地址转换为具体的代码行。

  • 关键步骤

    1. 保留崩溃版本对应的 so 库和符号表(编译时生成的 .sym 文件)。

    2. 使用 ndk-stack 工具解析:

      ndk-stack -sym /path/to/so/dir -dump /path/to/crash/log.txt
      
    3. 解析后的堆栈会显示具体的 C/C++ 函数和行号。

(3)ANR 分析

重点查看 traces.txt 中的 主线程堆栈,找到阻塞主线程的耗时操作。

  • 示例:主线程执行网络请求导致 ANR
"main" prio=5 tid=1 Waiting
  | group="main" sCount=1 dsCount=0 flags=1 obj=0x72a092a0 self=0xb4000070f1d80000
  | sysTid=1234 nice=0 cgrp=default sched=0/0 handle=0x779f4a4540
  | state=S schedstat=( 1234567 8765432 123 ) utm=100 stm=20 core=1 HZ=100
  | stack=0x7fd836a000-0x7fd836c000 stackSize=8192KB
  | held mutexes=
  at java.net.PlainSocketImpl.socketConnect(Native method)
  - waiting on a blocking call
  at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:387)
  at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:230)
  at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:212)
  at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:436)
  at java.net.Socket.connect(Socket.java:621)
  at com.example.myapp.MainActivity.fetchData(MainActivity.java:50)
  at com.example.myapp.MainActivity.onCreate(MainActivity.java:25)
  • 定位:第 50 行的 fetchData 方法在主线程执行网络请求,阻塞超过 5s。

3. 复现与验证

定位根因后,需要复现崩溃场景,验证修复方案是否有效:

  • 复现技巧:模拟相同的设备、系统版本、操作步骤;对于偶现崩溃,可增加日志或使用调试工具(如 Android Studio Profiler)。
  • 修复原则:针对根因修复,而非掩盖现象(如空指针不能只加 null 判断,要分析为何为空)。

Android Crash 的预防措施

最好的监控是预防,通过规范开发流程减少 Crash 发生:

  1. 严格的代码评审:重点检查空指针、类型转换、数组越界等高频问题。
  2. 主线程禁止耗时操作:网络请求、数据库读写、文件操作等必须放在子线程(使用 Coroutine、RxJava 或 AsyncTask)。
  3. 合理处理权限:Android 6.0+ 危险权限需动态申请,避免权限缺失导致崩溃。
  4. 使用安全的 API:如使用 String.valueOf() 代替直接调用 toString(),避免空指针。
  5. 内存优化:避免内存泄漏(如 Handler 持有 Activity 引用、静态变量持有上下文),使用 LeakCanary 检测内存泄漏。
  6. 测试覆盖:多机型、多系统版本测试;使用 Monkey 测试进行随机压力测试,发现偶现崩溃。

关键工具推荐

  1. LeakCanary:Square 开源的内存泄漏检测工具,可自动检测并上报内存泄漏。
  2. Android Studio Profiler:监控应用的 CPU、内存、网络使用情况,定位性能问题和崩溃诱因。
  3. Logcat:系统日志工具,实时查看应用运行日志和崩溃堆栈。
  4. ndk-stack:解析 Native 崩溃堆栈的官方工具。

Android Crash 排查速查清单

这份清单涵盖 Java/Kotlin 层、Native 层、ANR 三类崩溃的全流程排查步骤,可直接用于开发和测试阶段的问题定位。

前期准备:必备信息与工具

类别关键内容
必须收集的崩溃信息1. 崩溃堆栈(完整调用链)
2. 设备信息(机型、系统版本、SDK 版本)
3. 应用信息(版本号、包名、渠道)
4. 上下文信息(用户操作步骤、网络状态、内存占用)
5. 崩溃时间与频次(偶现 / 必现)
必备工具1. 基础工具:Android Studio Logcat、Profiler
2. Java 层:自定义 CrashHandler、Bugly/Firebase 后台
3. Native 层:ndk-stack、Breakpad、符号表(.sym 文件)
4. ANR 专用:traces.txt 读取工具、WatchDog 监控脚本
5. 辅助工具:LeakCanary(内存泄漏检测)

分类型排查步骤

1. Java/Kotlin 层 Crash(最高频)

步骤操作要点
1. 定位崩溃行直接查看堆栈最顶层行,格式:类名.方法名(文件名:行号)例:MainActivity.onCreate(MainActivity.java:20) → 第 20 行代码问题
2. 识别异常类型高频异常及解决方向:
- 空指针(NPE):检查对象是否初始化、是否为 null
- 数组越界:检查下标范围是否小于数组长度
- 类型转换失败:确认对象实际类型与强转类型是否匹配
- 资源未找到:检查资源 ID 是否写错、是否存在于对应 res 目录
3. 复现验证1. 模拟相同设备 + 系统版本 + 应用版本
2. 复现用户操作步骤
3. 若偶现:增加日志打印关键变量值
4. 修复原则不掩盖问题:如 NPE 不能只加 if (obj != null),要分析对象为何为空

2. Native 层 Crash(难度高)

步骤操作要点
1. 准备前提条件必须保留崩溃版本对应的 so 库和编译生成的符号表
2. 解析堆栈地址使用 ndk-stack 工具,命令示例:ndk-stack -sym /你的so库目录 -dump /崩溃日志文件.txt
3. 定位问题原因常见根因及解决:
- 非法内存访问:检查 JNI 层指针是否为空、是否越界
- 类型不匹配:核对 Java 类型与 JNI 类型映射(如 int ↔ jint)
- so 库版本兼容:确认 so 库是否适配当前 CPU 架构(armeabi-v7a/arm64-v8a)
4. 验证修复替换修复后的 so 库,重新打包测试

3. ANR(应用无响应)

步骤操作要点
1. 获取 ANR 日志方式 1:root 设备直接读取 /data/anr/traces.txt
方式 2:通过第三方平台(Bugly)获取上报的 traces 日志
方式 3:自研 WatchDog 监控主线程阻塞状态
2. 分析主线程堆栈重点看 main 线程的状态:
- Waiting/Blocked:线程被锁阻塞,检查死锁
- Running:执行耗时操作(网络 / 数据库 / 计算)
3. 定位耗时代码常见问题:主线程执行网络请求、数据库批量读写、复杂 UI 绘制
解决方向:将耗时操作移到子线程(Coroutine/RxJava)
4. 优化验证使用 Android Studio Profiler 监控主线程 CPU 占用率,确认耗时操作已迁移

通用修复与预防措施

阶段具体操作
修复后验证1. 必现崩溃:修复后需 100% 复现验证
2. 偶现崩溃:增加压力测试(如 Monkey 测试)
3. 回归测试:检查修复是否引入新问题
长期预防1. 代码规范:避免主线程耗时操作 - 空指针防御(String.valueOf() 代替 toString()) - 动态权限申请(Android 6.0+)
2. 监控体系:接入第三方崩溃监控平台 - 集成 LeakCanary 检测内存泄漏
3. 测试覆盖:多机型 / 多系统版本测试 - 灰度发布,提前发现问题

紧急排查技巧(线上崩溃)

  1. 优先看崩溃频次:高频崩溃需优先修复,影响大量用户
  2. 筛选关键设备:若仅特定机型崩溃,排查机型适配问题(如刘海屏 / 系统定制 ROM)
  3. 对比版本差异:若某版本突然新增崩溃,对比代码提交记录,定位新增代码
最近更新:: 2026/1/11 02:25
Contributors: luokaiwen