1. LifecycleObserver 未注销的风险场景
在 Android Jetpack 架构中,LifecycleObserver 用于监听 Activity/Fragment 的生命周期事件。如果注册后忘记在对应销毁时机(如 onDestroy)注销,会导致一系列连锁问题。本章梳理五个核心风险场景,帮助开发者建立“可注销则必注销”的心理模型。
1.1 风险场景总览
| 风险名称 | 触发原理 | 典型后果 | 性能/稳定性影响 |
|---|---|---|---|
| 内存泄漏 | Observer 持有 Activity 引用,Activity 被销毁后仍被 LifecycleRegistry 持有,GC 无法回收。 | OOM、缓慢积累、GC 频繁 | ★★★★★ |
| 空指针异常 | Observer 回调中访问已销毁的 View 或 Context。 | 应用崩溃或 UI 异常 | ★★★★☆ |
| 生命周期错乱 | 已销毁的组件仍然收到 ON_STOP / ON_DESTROY 事件,导致重复释放或逻辑紊乱。 |
状态不一致、数据重复加载 | ★★★☆☆ |
| ANR 触发 | Observer 回调中执行长时间操作(如网络请求),而主线程因组件消失没有及时终止。 | 界面无响应 5 秒以上 | ★★★★★ |
| 资源占用 | Observer 持有的相机、传感器、数据库游标等未被释放。 | 电池耗电、内存膨胀、连接耗尽 | ★★★★☆ |
1.2 深度分析
内存泄漏
最常见的风险。LifecycleRegistry 内部通过 ArrayList 持有所有注册的 Observer。若 Observer 隐式持有 Activity(如内部类或 Lambda),Activity 即使被销毁也无法释放。典型泄漏链:
Activity → LifecycleRegistry → Observer(内部类) → View/Context
此类泄漏通常不可立即发现,直到内存抖动或 OOM 才被注意。
空指针异常
Observer 回调(如 @OnLifecycleEvent(ON_STOP))可能尝试更新已离场的 UI。例如:
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun onResume() {
// 假设 mTextView 在 onDestroy 中已经被置 null
mTextView.text = "updated" // 抛出 NullPointerException
}
这种异常难以复现,通常只在快速切换页面时才触发。
生命周期错乱
已销毁的组件再次收到 ON_CREATE 或 ON_START 等事件(如果 Observer 被错误地复用在另一个未完全销毁的组件上),导致业务逻辑重复执行或资源双重释放。
ANR 触发
即使 Activity 已销毁,未注销的 Observer 仍可能继续接收广播或定时任务回调,若回调中执行耗时网络请求或数据库操作,主线程将被阻塞超过 5 秒,触发 ANR。
资源占用
CameraX、传感器、Room 数据库、蓝牙连接等资源如果在 Observer 回调中打开而忘记关闭,会持续占用系统资源,导致功耗上升或连接数耗尽。
1.3 风险关系图
1.4 代码演示:未注销导致的内存泄漏
以下代码展示一个常见的“忘记注销”场景:
class LeakActivity : AppCompatActivity() {
private val myObserver = MyLifecycleObserver(this)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycle.addObserver(myObserver)
// 注意:没有在 onDestroy 中 removeObserver
}
class MyLifecycleObserver(private val activity: Activity) : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun onResume() {
// 内部类持有 activity 引用
activity.window?.decorView?.let {
// 做一些 UI 操作
}
}
}
}
当 LeakActivity 销毁后,myObserver 仍被 LifecycleRegistry 持有,导致 Activity 无法被 GC 回收。同时 onResume 回调可能在已销毁的窗口上执行,引发空指针或状态错乱。
1.5 小结
LifecycleObserver 未注销是一个隐蔽但危害巨大的问题。本章从内存泄漏、空指针异常、生命周期错乱、ANR 触发、资源占用五个维度解构了风险场景。下一章开始,我们将逐一学习自动化检测方法、手动检测工具以及最佳实践修复策略。
DefaultLifecycleObserver 或 Kotlin 协程 Flow,未注销问题依然存在。请务必在 onDestroy 中执行 lifecycle.removeObserver(observer),或使用 LifecycleEventObserver 的自动管理机制(如 viewLifecycleOwner)。