优势与适用场景
在 Android 开发中,让 View
实现 LifecycleOwner
接口可以为视图赋予生命周期感知能力,从而更优雅地管理资源、数据绑定和异步操作。以下是其在实际开发中的典型用途和优势:
-
生命周期感知的数据观察
-
场景:
View
内部需要观察LiveData
或Flow
等数据流,但不想依赖外部的Activity
/Fragment
生命周期。 -
优势:
- 直接在
View
中通过lifecycleScope
启动协程,或通过observe(this)
观察数据。 - 当
View
被移除时,自动取消数据订阅或协程任务, 避免内存泄漏。
- 直接在
-
解耦视图逻辑
-
场景:开发可复用的自定义
View
,希望内部逻辑(如动画、网络请求)与宿主Activity
/Fragment
解耦。 -
优势:
View
自行管理生命周期,无需依赖外部传递LifecycleOwner
。- 提升组件独立性,降低与宿主页面的耦合度。
3. 精准控制资源释放
-
场景:
View
持有动画、传感器监听、Handler
等资源,需在适当时机释放。 -
优势:
- 通过
LifecycleObserver
的 在不同的时期精准控制资源启停。 - 避免因宿主页面生命周期过长导致资源泄漏。也减少手动重写View的各种与生命周期相关的函数,例如
onWindowVisibilityChanged
,onAttachedToWindow
- 通过
适用场景分析
-
静态View(与Activity生命周期一致) 如果View在Activity的整个生命周期内始终存在且不会被移除(如静态布局中的固定控件),直接使用Activity的
LifecycleOwner
即可。此时View的生命周期与Activity完全同步,无需额外实现。 -
动态View(可能被移除或替换) 需要自定义
LifecycleOwner
的典型场景包括:- Dialog:Dialog的显示与隐藏独立于Activity,需自行管理资源。
- 通过
FrameLayout
切换子View:被移除的View需及时释放资源。 - RecyclerView的ItemView:Item被回收时需停止内部任务。
第一版
技术背景
在数据库监听场景中,需基于视图可见性控制Flow数据收集行为:
// 仅当视图可见时激活数据流监听
flow.flowWithLifecycle(lifecycle)
.collect { data -> updateUI(data) }
为实现该特性,设计了可感知窗口可见性变化的LifecycleViewGroup
基类。
初始实现方案
/**
* 通过窗口可见性变化驱动生命周期状态的ViewGroup基类
*/
abstract class LifecycleViewGroup(context: Context) : ViewGroup(context), LifecycleOwner {
private val mRegistry = LifecycleRegistry(this)
override val lifecycle: Lifecycle get() = mRegistry
override fun onWindowVisibilityChanged(visibility: Int) {
super.onWindowVisibilityChanged(visibility)
when (visibility) {
View.VISIBLE -> {
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
}
else -> {
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE)
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
}
}
}
}
方案缺陷分析
1. 生命周期状态未完全终结
视图从窗口分离时仅触发ON_STOP
而非ON_DESTROY
,导致以下问题:
- 协程任务泄漏
lifecycleScope
管理的轮询任务在视图不可见后仍持续运行
// 弹窗关闭后轮询接口任务未终止
lifecycleScope.launch {
while (true) {
fetchData() // 后台持续执行
delay(5000)
}
}
- LiveData内存泄漏 LiveData持有了处于
CREATED
状态的视图引用,无法被回收
viewModel.data.observe(this@LifecycleViewGroup) {
// 即使视图不可见,Observer仍被保留
}
2. 典型问题场景复现
场景 | 现象 | 后果 |
---|---|---|
弹窗轮询网络 | 关闭弹窗后轮询任务持续执行 | 资源浪费 & 数据篡改风险 |
动态移除子视图 | 被移除视图的LiveData绑定未解除 | 内存泄漏 & 无效UI更新 |
页面跳转时动画未停止 | 不可见视图的动画持续消耗CPU | 性能下降 & 电池损耗 |
第二版
/**
* 具备完整生命周期管理的ViewGroup基类
* 通过窗口附着状态驱动ON_CREATE/ON_DESTROY事件
* 通过窗口可见性驱动ON_START/ON_STOP等中间状态事件
*/
abstract class LifecycleViewGroup(
context: Context,
attrs: AttributeSet? = null
) : ViewGroup(context, attrs), LifecycleOwner {
// 生命周期状态注册器
private val mRegistry = LifecycleRegistry(this)
override val lifecycle: Lifecycle get() = mRegistry
/**
* 窗口可见性变化监听
* 可见性状态与Activity标准生命周期事件对齐
*/
override fun onWindowVisibilityChanged(visibility: Int) {
super.onWindowVisibilityChanged(visibility)
when (visibility) {
View.VISIBLE -> {
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
}
else -> {
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE)
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
}
}
}
/**
* 视图挂载时初始化生命周期
* 模拟Activity的onCreate阶段
*/
override fun onAttachedToWindow() {
super.onAttachedToWindow()
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
}
/**
* 视图卸载时终结生命周期
* 触发资源释放与观察者解绑
*/
override fun onDetachedFromWindow() {
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
super.onDetachedFromWindow()
}
}
方案优势分析
优势维度 | 技术原理 | 典型收益场景 |
---|---|---|
内存泄漏防护 | ON_DESTROY触发LiveData自动解除观察,释放对ContentView的强引用 | 弹窗关闭后立即释放关联数据模型 |
协程任务自动管理 | lifecycleScope随DESTROY状态自动取消,避免后台任务泄漏 | 轮询请求、动画控制等异步操作 |
生命周期完整性 | 完整实现CREATE→START→RESUME→PAUSE→STOP→DESTROY标准状态流转 | 与Jetpack组件生命周期感知体系无缝对接 |
潜在缺陷说明
视图复用场景副作用
// 在RecyclerView等复用场景中可能产生异常
class RecyclerViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
init {
// 视图被回收后重新attach时,初始化逻辑不会执行, 像liveData被移除的观察者不会自动恢复
viewModel.liveData.observer(this) { ... }
// detch 后 lifecycleScope被杀死了不能恢复
lifecycleScope.launch {
while(isActive) { ... }
}
}
}
影响表现:
- 复用视图的初始化逻辑(
init{}
代码块)仅在首次创建时执行 - 后续复用周期中,
onAttachedToWindow
触发的ON_CREATE事件不会重新触发初始化,且 lifecycleScope被杀死了无法恢复,在之前lifecycleScope.launch{ xxx }
任务被关闭了不会恢复,在之后lifecycleScope.launch{ xxx }
也不能启动一个协程
第三版
/**
* 支持视图复用的生命周期感知容器基类
* 核心机制:通过生命周期状态判断触发Registry重建,实现资源隔离
*/abstract class LifecycleViewGroup(context: Context) : ViewGroup(context), LifecycleOwner {
private var mRegistry = LifecycleRegistry(this)
override val lifecycle: Lifecycle get() = mRegistry
/**
* 强制约束观察者绑定入口
* 规避因复用导致的观察者残留问题
*/abstract fun bindObservers()/**
* 窗口附着时执行状态重置
* 采用≤DESTROYED作为复用视图的判定条件
*/override fun onAttachedToWindow() {
super.onAttachedToWindow()
if (lifecycle.currentState <= Lifecycle.State.DESTROYED) {
mRegistry = LifecycleRegistry(this) // 重建生命周期注册器
}
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
bindObservers() // 统一入口重建观察者
}
}
设计约束说明
- 观察者绑定限制
// 允许的写法(在bindObservers内)
class CustomViewGroup : LifecycleViewGroup {
override fun bindObservers() {
viewModel.data.observe(this) { /*...*/ } // ✅ 正确
}
}
// 禁止的写法(在构造函数/其他位置)
class CustomViewGroup : LifecycleViewGroup {
init {
viewModel.error.observe(this) { /*...*/ } // ❌ 复用后失效
}
}
约束原因: 复用视图重建Registry时,在bindObservers外旧观察者未自动重新绑定到新Lifecycle实例
典型问题场景
(无法兼容)
fun setLiveData ( data : MutableLiveData < Int >) { modeIndex = data data .observe( this ) { // ❌ 无法保证在bindObservers内执行 } }
第四版
object ViewLifecycleManager {
private val viewSet = HashSet<Int>()
fun get(view: View): ViewLifecycleOwner {
require(!viewSet.contains(view.hashCode())) { "The provided view is registered in the ViewLifecycleManager." }
viewSet.add(view.hashCode())
return object : ViewLifecycleOwner(view) {}
}
/**
* 抽象类为了让外部不可以直接new一个对象, 必须走`ViewLifecycleManager.get(view)`
*/
abstract class ViewLifecycleOwner(view: View) {
private val observers = mutableListOf<LifecycleOwner.() -> Unit>()
private val lifecycleOwnerInner = ViewLifecycleOwnerInner(view) {
observers.forEach {
it.invoke(this)
}
}
open fun bindObserver(observer: LifecycleOwner.() -> Unit) {
observers.add(observer)
if (lifecycleOwnerInner.lifecycle.currentState >= Lifecycle.State.CREATED) {
observer.invoke(lifecycleOwnerInner)
}
}
fun launchMain(action: suspend CoroutineScope.() -> Unit) {
lifecycleOwnerInner.lifecycleScope.launchMain(action)
}
fun launchIO(action: suspend CoroutineScope.() -> Unit) {
lifecycleOwnerInner.lifecycleScope.launchIO(action)
}
/**
* 实现 LifecycleOwner 接口的类,用于确保使用 ViewLifecycleOwner 时的正确性。
*
* 该类的目的是使得以下代码:
*
* ```
* val owner = ViewLifecycleManager.get(xx)
* livedata.observe(owner) { xxx }
* ```
*
* 报错,从而强制用户使用更安全的方式来绑定观察者。
*
* 用户必须通过调用 `owner.bindObserver { }` 来注册观察者
* 以确保在生命周期被正确观察
*/
private class ViewLifecycleOwnerInner(
view: View,
val bindObserver: LifecycleOwner.() -> Unit
) : LifecycleOwner,
View.OnAttachStateChangeListener {
private var mRegistry = LifecycleRegistry(this)
private val activityLifecycle: Lifecycle? = (view.context as? LifecycleOwner)?.lifecycle
private val activityObserver = LifecycleEventObserver { _, event ->
if (mRegistry.currentState >= Lifecycle.State.CREATED) {
mRegistry.handleLifecycleEvent(event)
}
}
override val lifecycle: Lifecycle
get() = mRegistry
init {
// 因为调用方可能会在view已经AttachedToWindow后再去获得ViewLifecycleOwner
// 这时候`addOnAttachStateChangeListener`的 `onViewAttachedToWindow`不会被触发,状态仍然处于`INITIALIZED`
if (view.isAttachedToWindow) {
onViewAttachedToWindow(view)
}
view.addOnAttachStateChangeListener(this)
}
override fun onViewAttachedToWindow(v: View) {
if (mRegistry.currentState <= Lifecycle.State.DESTROYED) {
mRegistry = LifecycleRegistry(this@ViewLifecycleOwnerInner)
}
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
activityLifecycle?.addObserver(activityObserver)
bindObserver.invoke(this@ViewLifecycleOwnerInner)
}
override fun onViewDetachedFromWindow(v: View) {
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
activityLifecycle?.removeObserver(activityObserver)
}
}
}
}
旧版问题 开发者可能在任意位置(如 setLiveData
)添加观察者,导致无法保证生命周期一致性:
fun setLiveData(data: LiveData<Int>) {
data.observe(this) { /* 可能绑定到已销毁的 Lifecycle */ } // ❌
}
新版本通过ViewLifecycleManager.get(view) 获得一个view的ViewLifecycleOwner
由于ViewLifecycleOwner 不是 lifecycleOwner的实现类
这样的代码会报错,必须通过调用 owner.bindObserver { }
来注册观察者
val owner = ViewLifecycleManager.get(xx)
livedata.observe(owner) { xxx }
另外之前的版本是特定的ViewGroup基类,必须得是自己写的自定义view,而新版本提供了一个更灵活的解决方案,可以用于任何View,而不仅仅是ViewGroup的子类。
生命周期状态对照表
生命周期事件 | |
---|---|
首次附加到窗口 | ON_CREATE → ON_START |
从窗口分离 | ON_DESTROY |
Activity配置变更 | ON_PAUSE → ON_STOP |
Activity返回前台 | ON_START → ON_RESUME |
方案对比
旧版方案 | 新版方案 | |
---|---|---|
适用范围 | 仅限自定义View | 支持任意View |
观察者安全 | 可能绑定到无效生命周期 只能在特定位置绑定 | 强制通过bindObserver 可以在任何位置绑定 |
有无观察view本身可见性 | ✅ | ❌ |
直接操控lifecycle | ✅ | ❌ |