可通过 Thread.setUncaughtExceptionHandler() 拦截未捕获异常,实现日志收集、异步上传与安全重启;需全局注册、结构化采集上下文、避免主线程操作、防重启循环,并兼顾多进程与调试降级。

可以通过 Thread.setUncaughtExceptionHandler() 拦截未捕获异常,在崩溃发生时执行自定义逻辑,实现日志收集、上传与进程重启。关键在于:异常处理需在主线程(UI线程)和子线程统一注册;日志需包含堆栈、设备信息、时间戳等上下文;上传应异步且带失败重试;重启需避免无限循环,建议用 AlarmManager 或 WorkManager 延迟拉起。
全局注册异常处理器
在 Application 的 onCreate() 中为默认线程设置处理器,并遍历已有线程(如主线程)确保覆盖:
- 调用
Thread.setDefaultUncaughtExceptionHandler()设置全局兜底处理器 - 显式对主线程(
Looper.getMainLooper().getThread())再次设置,防止被系统框架覆盖 - 若使用多进程,每个进程的 Application 都需单独注册
异常处理中完成日志采集与本地落盘
在 uncaughtException(Thread t, Throwable e) 回调里,立即生成结构化崩溃日志:
- 获取完整堆栈(
Log.getStackTraceString(e)),避免只取e.toString() - 补充运行时信息:App 版本、Android SDK 版本、设备型号、内存状态(
ActivityManager.MemoryInfo)、前台 Activity 名称(通过ActivityManager.getRunningTasks()或 LifecycleObserver 记录) - 将日志写入私有目录下的临时文件(如
getCacheDir()/crash_20240520_142345.log),使用FileOutputStream并设为MODE_PRIVATE,避免被其他应用读取
异步上传崩溃日志并安全重启
切勿在异常回调中直接发起网络请求或启动 Activity —— 此时 Looper 可能已终止,UI 线程处于不可靠状态:
- 启动一个独立的
IntentService或JobIntentService(推荐),传递日志文件路径,在后台线程中执行上传 - 上传成功后删除本地日志;失败则保留并记录失败次数(可存 SharedPreferences),后续启动时检查并重试
- 重启采用「冷启动」方式:用
AlarmManager.setExactAndAllowWhileIdle()设置 1–3 秒后唤醒,触发一个启动 Launcher Activity 的 PendingIntent;避免使用System.exit(0)或Process.killProcess(),它们无法保证 Activity 重建
防崩溃循环与降级策略
若崩溃由初始化逻辑反复触发(如 Application 构造器抛异常),需防止自动重启导致死循环:
- 在 Application
onCreate()开头读取 SharedPreferences 中的「崩溃计数」和「上次崩溃时间」,若 5 分钟内崩溃 ≥ 3 次,跳过自动重启,仅上报日志 - 上传服务中增加网络判断:无网络时不尝试上传,等待下次启动再处理;上传接口需支持幂等性,避免重复日志
- 调试阶段可配置开关(如 BuildConfig.DEBUG),禁用自动重启,方便断点分析
不复杂但容易忽略:Handler 切换、文件权限、ANR 干扰、多进程日志隔离。把异常捕获当作一次“最后的机会”,重点是稳住现场、留好证据、可控恢复。










