2. 崩溃日志采集与解读:Logcat定位、native堆栈、ANR trace分析

当 CameraX 出现崩溃或 ANR 时,快速采集正确的日志是修复的第一步。本章将拆解三种崩溃类型(Java Crash、Native Crash、ANR)的现象、日志特征与定位方法,并通过故障树和排查表帮助你系统化定位根因。

2.1 现象识别

崩溃发生时,首先需要判断其类型。下表总结了三种崩溃的典型表象:

崩溃类型现象关键日志关键字系统表现
Java Crash应用直接闪退,弹窗“xxx已停止运行”FATAL EXCEPTION、java.lang.*进程终止,返回桌面
Native Crash闪退无弹窗,或出现SIGSEGV等信号signal 11、tombstone_*进程被系统杀掉,可抓取tombstone
ANR应用卡死后弹窗“xxx无响应”ANR in xxx、cpu=、waiting on应用未退出但无响应
快速判断规则: Logcat 中搜索 FATAL EXCEPTION 为 Java Crash;搜索 signal 后跟数字且不含 java 堆栈为 Native Crash;搜索 ANR in 为 ANR。

2.2 数据采集方法与命令

在问题复现时,应立即采集以下三类日志:

2.2.1 Logcat 实时日志

使用 adb 命令捕获全量日志,建议加上时间戳和进程过滤:

# 清空旧日志后启动实时捕获(按Ctrl+C停止)
adb logcat -c && adb logcat -v threadtime -s "CameraX:*" "ActivityManager:*" "*:E" > crash_log.txt

# 过滤指定 pid(如果知道进程号)
adb logcat -v threadtime --pid=1234 > pid_1234_log.txt

2.2.2 Native 崩溃 tombstone 文件

Native Crash 发生后,系统在 /data/tombstones/ 目录生成 tombstone_xx 文件,包含寄存器、调用栈和内存映射。使用以下命令提取:

# 查看所有tombstone
adb shell ls -l /data/tombstones/

# 拉取最新tombstone
adb pull /data/tombstones/tombstone_00 ./tombstone_00.txt

2.2.3 ANR Trace 文件

ANR 发生时,系统在 /data/anr/traces.txt 记录所有线程状态。使用如下方法:

# 普通权限设备(需要 root 或 debuggable)
adb pull /data/anr/traces.txt ./anr_traces.txt

# 无 root 的非 debuggable 设备,可通过以下方式触发生成(部分系统支持)
adb shell am force-stop com.example.app
# 等待 ANR 后使用 bugreport
adb bugreport
# 解压后在 FS/data/anr/traces.txt 中找到
注意事项:ANR trace 文件在非 root 设备上可能无法直接 pull,优先使用 bugreport 方式获取完整报告。

2.3 原因定位:故障树与排查表

使用故障树可以结构化地分析崩溃根因。下图以 CameraX 崩溃场景为例:

CameraX 崩溃故障树 CameraX 崩溃 Java Crash Native Crash ANR 空指针 无效状态转换 内存访问越界 Camera HAL 崩溃 主线程磁盘 I/O Binder 调用超时 排查建议 • Java Crash:查看 FATAL EXCEPTION 完整堆栈,定位到具体行号。 • Native Crash:解析 tombstone 中的 pc 地址和 libcamera_xxx.so 偏移。 • ANR:分析 traces.txt 中主线程状态,查找 "waiting on" 或 "blocked" 关键字。

2.3.1 排查表

以下排查表帮助根据日志特征快速定位根因类型:

日志特征可能的根因推荐动作
java.lang.NullPointerException + CameraX API 调用未检查 CameraX 生命周期绑定状态添加空值检查,使用 ProcessCameraProvider.getInstance() 回调
signal 11 (SIGSEGV) 且 pc 落在 libcamera_client.soCamera HAL 内部指针错误检查设备兼容性,降级 CameraX 版本或使用 Camera2 备用
ANR + 主线程在 ImageCapture.takePicture 上阻塞拍照回调中执行耗时操作将回调切换到后台线程,使用 Executor 或 ListenableFuture
FATAL EXCEPTION: main + CameraXConfig 相关配置初始化时线程问题确保 CameraXConfig 在主线程调用前完成
关键原理: CameraX 内部使用 ListenableFuture 和 HandlerThread 管理异步操作。如果主线程直接等待 Future 结果会导致 ANR;如果 Native 层访问已释放的内存则导致 SIGSEGV。理解这些设计模式有助于快速定位日志。

2.4 修复动作

基于日志定位后,针对常见根因的修复示例:

  • Java Crash - 空指针: 在 ProcessCameraProvider.unbindAll() 后立即调用 bindToLifecycle() 前,检查 provider 是否已关闭。
  • Native Crash - Camera HAL 崩溃: 尝试使用 CameraSelector 的 DEFAULT_BACK_CAMERA 替换为 CAMERA_FACING_FRONT 测试,排除硬件问题。若为特定设备,添加黑名单。
  • ANR - 主线程阻塞: 将 takePicture 或 setAnalyzer 的回调通过 Executors.newSingleThreadExecutor() 执行。
// 修复ANR示例:使用后台线程处理拍照结果
ImageCapture capture = new ImageCapture.Builder().build();
capture.takePicture(context,
    Executors.newSingleThreadExecutor(),
    new ImageCapture.OnImageCapturedCallback() {
        @Override
        public void onCaptureSuccess(@NonNull ImageProxy image) {
            // 处理图像(不在主线程)
        }
    });

2.5 复测

  1. 在已知崩溃的设备/系统上复现场景,确保不再出现相同日志。
  2. 执行压力测试:连续点击拍照、切换摄像头、旋转屏幕、按Home键再返回,每个动作间隔200ms,循环100次。
  3. 检查 Logcat 中无 FATAL EXCEPTION、无 signal 相关 warning、无 ANR。
  4. 若原因为 Native,验证 tombstone 不再生成。

2.6 预防措施

  • 在应用发布版本中开启 android:debuggable="false" 并集成崩溃日志自动上传(如 Firebase Crashlytics)。
  • 对 CameraX 关键调用加 try-catch (RuntimeException),将堆栈写入自有日志文件。
  • 在 CI/CD 流程中集成 Monkey 测试和 ANR 预检测脚本。
# 自动化ANR检测脚本片段(bash)
adb shell dumpsys activity processes | grep "anr" > anr_check.txt
if [ -s anr_check.txt ]; then
    echo "ANR detected"
    adb pull /data/anr/traces.txt ./ci_anr_$(date +%s).txt
fi
最佳实践: 在测试阶段就开启 adb logcat -v threadtime -b all 记录全量日志,配合 strace 可进一步追踪native调用。对于 CameraX,推荐在 CameraXConfig 中设置 setCameraExecutor 和 setSchedulerHandler 来避免线程饥饿。