应用稳定性治理
场景
线上崩溃率从 0.1% 飙升到 0.5%,需要在一周内降回 0.2% 以下。
排查与方案
1. 崩溃分类
从 Crashlytics / Bugly 后台按类型和影响面排序:
| 类型 | 占比 | 优先级 |
|---|---|---|
| Java Crash (NullPointerException) | 40% | P0 |
| Java Crash (其他) | 20% | P1 |
| Native Crash (SIGSEGV) | 15% | P1 |
| ANR | 25% | P0 |
2. Top Crash 止血
// 典型 NPE 场景:Lifecycle 不同步
class DetailFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// ❌ 异步回调时 Fragment 可能已 detach
viewModel.data.observe(viewLifecycleOwner) { data -> // ✅ 用 viewLifecycleOwner
binding.title.text = data.title // binding 可能为 null
}
}
// ✅ 安全访问 binding
private var _binding: FragmentDetailBinding? = null
private val binding get() = _binding!!
override fun onDestroyView() {
super.onDestroyView()
_binding = null // 防止内存泄漏
}
}
3. ANR 治理
ANR 的主要原因是主线程被阻塞超过 5 秒:
| ANR 原因 | 解决方案 |
|---|---|
| 主线程数据库查询 | 移到 Dispatchers.IO,用 Room 的 suspend 函数 |
SharedPreferences commit() | 改用 apply()(异步写入) |
| 主线程文件 I/O | 移到后台线程 |
| 死锁 | 统一锁顺序,减小锁粒度 |
| BroadcastReceiver 超时 | 耗时操作交给 goAsync() 或 WorkManager |
4. 防劣化机制
// 全局兜底:捕获未处理的异常防止闪退(仅用于非致命场景)
Thread.setDefaultUncaughtExceptionHandler { thread, throwable ->
if (isRecoverable(throwable)) {
// 记录日志,不杀进程
CrashLogger.log(throwable)
// 尝试恢复到主页
restartMainActivity()
} else {
// 真正的致命错误,交给系统处理
defaultHandler.uncaughtException(thread, throwable)
}
}
不要滥用全局兜底
全局捕获异常只是止血手段,不是长期方案。吞掉异常会隐藏 bug,应该结合监控确保每个异常都被修复。
面试答题要点
- 用崩溃后台排序 Top Crash,优先修复影响面最大的
- NPE 治理靠 Kotlin 空安全 + Lifecycle 感知
- ANR 治理的核心是不在主线程做 I/O
- 建立防劣化机制:代码审查 + CI 检测 + 线上监控