Android View生命周期感知实践:ViewLifecycleOwner的优势、演进与方案对比

137 阅读7分钟

优势与适用场景

在 Android 开发中,让 View 实现 LifecycleOwner 接口可以为视图赋予生命周期感知能力,从而更优雅地管理资源、数据绑定和异步操作。以下是其在实际开发中的典型用途和优势:

  1. 生命周期感知的数据观察

  • 场景View 内部需要观察 LiveDataFlow 等数据流,但不想依赖外部的 Activity/Fragment 生命周期。

  • 优势

    • 直接在 View 中通过 lifecycleScope 启动协程,或通过 observe(this) 观察数据。
    • View 被移除时,自动取消数据订阅或协程任务, 避免内存泄漏。
  1. 解耦视图逻辑

  • 场景:开发可复用的自定义 View,希望内部逻辑(如动画、网络请求)与宿主 Activity/Fragment 解耦。

  • 优势

    • View 自行管理生命周期,无需依赖外部传递 LifecycleOwner
    • 提升组件独立性,降低与宿主页面的耦合度。

3. 精准控制资源释放

  • 场景View 持有动画、传感器监听、Handler 等资源,需在适当时机释放。

  • 优势

    • 通过 LifecycleObserver 的 在不同的时期精准控制资源启停。
    • 避免因宿主页面生命周期过长导致资源泄漏。也减少手动重写View的各种与生命周期相关的函数,例如 onWindowVisibilityChanged , onAttachedToWindow

适用场景分析

  1. 静态View(与Activity生命周期一致) 如果View在Activity的整个生命周期内始终存在且不会被移除(如静态布局中的固定控件),直接使用Activity的LifecycleOwner即可。此时View的生命周期与Activity完全同步,无需额外实现。

  2. 动态View(可能被移除或替换) 需要自定义LifecycleOwner的典型场景包括:

    1. Dialog:Dialog的显示与隐藏独立于Activity,需自行管理资源。
    2. 通过 FrameLayout 切换子View:被移除的View需及时释放资源。
    3. 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() // 统一入口重建观察者
    }
}

设计约束说明

  1. 观察者绑定限制
// 允许的写法(在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