Android 生命周期监听:演进史与对应实现
用途:梳理 Android 生命周期监听机制的发展脉络、对应时代的实现方式,以及为什么会发生这些演进。理解了「痛点与解法」,写代码时自然就能选对 API。
建议阅读顺序:先看 一、当前最佳实践速查(结论),再按时间线阅读「为什么这么发展」。
一、当前最佳实践速查(结论)
不管历史怎么变,现在写代码请认准以下规范:
| 场景 | 当前推荐做法 | 淘汰/避坑的做法 |
|---|---|---|
| 写生命周期感知的组件(如相机、传感器) | 实现 DefaultLifecycleObserver,用 lifecycle.addObserver() 注册 | ❌ 不再使用 @OnLifecycleEvent 注解 |
| 在 Fragment 中操作 UI(如更新 TextView) | 必须绑定 viewLifecycleOwner.lifecycle | ❌ 不要绑定 Fragment 自身的 lifecycle |
| Kotlin 中收集 Flow / StateFlow | viewLifecycleOwner.lifecycleScope + repeatOnLifecycle(STARTED) | ❌ 不要直接在协程里裸 collect(后台耗电) |
| 进程级前后台判断 | ProcessLifecycleOwner.get().lifecycle | ❌ 不要在 BaseActivity 里自己计数 |
二、第一阶段:手写回调的「刀耕火种」时代
1. 对应实现
在 Activity / Fragment 的生命周期方法中,手动调用业务组件的注册与注销。
class MyActivity : AppCompatActivity() {
private val locationListener = MyLocationListener()
override fun onStart() {
super.onStart()
locationListener.start() // 手动开始
}
override fun onStop() {
locationListener.stop() // 手动停止
super.onStop()
}
}
2. 为什么会演进?(痛点)
- God Activity(上帝类):UI 控制器里塞满了各种无关业务(定位、网络、视频播放)的启停逻辑,代码极度臃肿。
- 极易出错:
onStart和onStop必须对称。一旦开发者忘记写注销代码,就会导致内存泄漏或后台崩溃。 - 组件无法独立:业务组件无法做到「自给自足」,必须依赖外部 UI 控制器主动通知它。
三、第二阶段:AAC Lifecycle 诞生与注解时代(2017)
为了解决上述痛点,Google 推出了 Android Architecture Components (AAC),引入了 Lifecycle 和 LifecycleOwner。组件终于可以「自己监听」生命周期了。
1. 对应实现
通过 @OnLifecycleEvent 注解标记方法,实现解耦。
// 组件自己管理自己
class MyLocationListener : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun start() { /* 开始定位 */ }
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stop() { /* 停止定位 */ }
}
// UI 层只管注册,其他不用管
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycle.addObserver(MyLocationListener())
}
}
2. 为什么当时不直接用接口?(历史局限)
2017 年 AAC 刚发布时,Android 主流环境还是 Java 7。
如果当时使用接口(比如包含 onCreate 到 onDestroy 6 个方法的接口),任何实现该接口的类都必须写出这 6 个方法的空实现,哪怕它只需要监听 onStart。这会导致大量冗余的样板代码。如果不这么做,就得提供一个 Adapter 抽象类让大家去继承,但这又会吃掉 Java 宝贵的「单继承」名额。
为了让代码保持简洁且不占用继承位,Google 选择了通过注解 @OnLifecycleEvent 让开发者「按需标记」方法。
3. 为什么后来又演进并废弃了注解?(痛点)
随着 Android 环境的升级,注解机制的劣势逐渐暴露:
- 性能开销:注解需要通过反射(运行时开销)或者注解处理器(APT/KAPT)(拖慢编译速度)来解析。
- Java 8 与 Kotlin 的普及:这两者都原生支持了接口默认方法(Default Methods)。现在接口里可以提供空的默认实现,开发者只需
override关心的那一个方法。这使得当初引入注解的最大理由(避免冗余方法实现)不复存在。 - 注:
@OnLifecycleEvent已在 Lifecycle 2.4.0 中被正式废弃。
四、第三阶段:接口化时代(现代 Java/Kotlin 标配)
1. 对应实现
废弃注解,全面转向显式的接口回调:DefaultLifecycleObserver 或 LifecycleEventObserver。
class MyLocationListener : DefaultLifecycleObserver {
// 接口自带 default empty implementation,按需重写即可
override fun onStart(owner: LifecycleOwner) { /* 开始定位 */ }
override fun onStop(owner: LifecycleOwner) { /* 停止定位 */ }
}
// 注册方式不变
lifecycle.addObserver(MyLocationListener())
2. 为什么这么发展?
- 零反射、编译快:直接的方法重写,没有任何魔法,性能最好。
- 类型安全、契约明确:方法签名是固定的,不会像注解时代那样方法名随便起、参数随便传。
五、支线演进:Fragment 的「视图生命周期」分离 (2018)
在 Fragment 的使用中,开发者遇到了一个致命的坑:Fragment 的实例生命周期 和 Fragment 内部 View 的生命周期 是不一致的。
1. 痛点场景
当 Fragment 被放入返回栈(BackStack)时,它的 View 会被销毁(调用 onDestroyView),但 Fragment 实例本身还活着。
如果在此时,某个绑在 Fragment lifecycle 上的网络请求回来了,去更新已经被销毁的 TextView,就会直接 Crash(NullPointerException)或者发生内存泄漏。
2. 演进后的实现:viewLifecycleOwner
Google 引入了 viewLifecycleOwner,专门代表 Fragment 内部 View 的生命周期。只要是操作 UI 的监听,必须绑定到它上面。
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// ✅ 正确:当 View 销毁时,这个 Observer 就会自动解绑
viewLifecycleOwner.lifecycle.addObserver(MyUIObserver())
// ❌ 错误:如果 Fragment 在返回栈里,View 已经没了,这个 Observer 还在跑
// lifecycle.addObserver(MyUIObserver())
}
六、第四阶段:协程与 Flow 时代 (2021 至今)
在响应式编程时代,LiveData 曾经是王,因为它自带生命周期感知。但随着 Kotlin 协程和 Flow 的崛起,LiveData 的能力显得不够用了(不支持复杂操作符、必须在主线程等)。
当大家转向 Flow 时,发现 Flow 的 collect 是不感知生命周期的——如果应用退到后台,Flow 还在默默收集,会疯狂消耗 CPU 和电量。
1. 早期妥协实现(已淘汰)
开发者被迫写丑陋的样板代码,在 onStart 里启动协程收集,在 onStop 里取消协程,一夜回到「刀耕火种」时代。
2. 最终演进实现:repeatOnLifecycle
Google 推出了 repeatOnLifecycle API,完美结合了协程的特性与生命周期感知。
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// 1. 绑定视图生命周期的协程作用域
viewLifecycleOwner.lifecycleScope.launch {
// 2. 声明:当生命周期至少到达 STARTED 时,执行 block;
// 当生命周期低于 STARTED(退到后台)时,取消 block;
// 再次回到前台时,重新执行 block。
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect { state ->
// 更新 UI
}
}
}
}
3. 为什么这么发展?
- 既享受了 Flow 强大的流式处理能力,又像 LiveData 一样做到了**「后台自动暂停、前台自动恢复」**,彻底解放了开发者,防止了后台资源浪费。
七、总结:演进的底层逻辑
Android 生命周期监听的发展,其实是一条主线:「把复杂的生命周期管理,从 UI 层剥离,下沉到框架和组件内部」。
- 解耦:从
Activity.onStart手写 →LifecycleOwner让组件自己管自己。 - 性能与规范:从
@OnLifecycleEvent注解反射 →DefaultLifecycleObserver接口重写。 - 精准与安全:从粗放的 Fragment
lifecycle→ 专门针对 UI 的viewLifecycleOwner。 - 适应现代语言:从 LiveData 的强绑定 →
repeatOnLifecycle结合 Kotlin Flow 的优雅挂起与恢复。
八、补充说明:语言演进史对框架设计的影响(番外篇)
把 Android Lifecycle API 的设计放到当时的语言环境时间轴上,很多「不合理」的设计就变得顺理成章:
- 2014 年前(Java 6/7 时代):没有「接口默认方法(Default Methods)」。如果用接口设计,任何实现类都必须重写从
onCreate到onDestroy所有的回调,产生巨量冗余代码。 - 2017 年(AAC 诞生,被困在 Java 7):当时 Android 主流环境仍是 Java 7,Google 为了不让开发者写样板代码,也为了不占用单继承名额,被迫采用
@OnLifecycleEvent注解 + 反射(或 APT)的妥协方案。 - 2017 - 2019 年(Kotlin 崛起):Google 宣布 Kotlin 成为一等公民,随后推行 Kotlin First。Kotlin(以及后来完善的 Android Java 8 脱糖技术)原生支持了接口默认实现。
- 2021 年(还技术债):在主流环境全面拥抱 Kotlin 和 Java 8 特性后,当初引入注解的最大理由(避免写一堆空方法)不复存在。官方在 Lifecycle 2.4.0 中果断废弃了臃肿且拖慢性能的注解方案,全面回归零反射、类型安全的
DefaultLifecycleObserver接口。