Android Lifecycle、LifecycleOwner、ViewLifecycleOwner、LifecycleScope、ViewModelScop

81 阅读5分钟

在 Android 开发中,管理组件(如 Activity 和 Fragment)的生命周期是一项基础且重要的工作。Jetpack 提供了一系列与生命周期相关的组件,帮助我们更安全、更高效地处理生命周期和协程任务。下面我们将详细讲解 LifecycleLifecycleOwnerViewLifecycleOwnerLifecycleScopeViewModelScope 以及 repeatOnLifecycle,并理清它们之间的关系。


🧬 Lifecycle

Lifecycle 是一个持有组件(如 Activity 或 Fragment)生命周期状态信息的类,并允许其他对象观察此状态。它使用 事件状态 两个枚举来跟踪生命周期。

  • 状态(State):当前生命周期所处的位置,例如 CREATEDSTARTEDRESUMEDDESTROYED
  • 事件(Event):生命周期发生变化时触发的事件,例如 ON_CREATEON_STARTON_RESUMEON_PAUSEON_STOPON_DESTROY

Lifecycle 的核心机制是 观察者模式:你可以通过 lifecycle.addObserver(LifecycleObserver) 注册观察者,在生命周期事件发生时收到回调。LifecycleOwner 则提供了 getLifecycle() 方法来获取 Lifecycle 对象。


👤 LifecycleOwner

LifecycleOwner 是一个接口,表示实现该接口的类拥有一个 Lifecycle 对象。Android 中常见的 LifecycleOwner 包括 AppCompatActivityFragment。它们通过 getLifecycle() 返回自身的 Lifecycle 实例。

例如,在 Activity 或 Fragment 中,我们可以这样获取 Lifecycle:

class MyActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        lifecycle.addObserver(MyObserver())
    }
}

LifecycleOwner 的存在使得我们可以在任何持有生命周期的地方统一管理生命周期感知组件。


🖼️ ViewLifecycleOwner

ViewLifecycleOwnerFragment 特有的概念。Fragment 本身是一个 LifecycleOwner,但它还拥有另一个生命周期:Fragment 视图的生命周期(从 onCreateViewonDestroyView)。ViewLifecycleOwner 就是代表这个视图生命周期的 LifecycleOwner

为什么需要它?因为 Fragment 的实例可能存活更久,而视图可能会被多次创建和销毁(例如横竖屏切换、添加到返回栈等)。在更新 UI 或收集数据时,我们通常应该绑定到视图的生命周期,而不是 Fragment 本身,以避免在视图销毁后更新 UI 导致崩溃。

例如,在 Fragment 中收集数据流时,官方推荐使用 viewLifecycleOwner.lifecycleScopeviewLifecycleOwner.repeatOnLifecycle

class MyFragment : Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        viewLifecycleOwner.lifecycleScope.launch {
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.uiState.collect { state ->
                    // 更新 UI,只有视图处于 STARTED 状态时才会执行
                }
            }
        }
    }
}

如果错误地使用了 this.lifecycleScope(即 Fragment 本身的 Lifecycle),协程会在 Fragment 被销毁时取消,但视图可能早已销毁,导致 IllegalStateException(尝试在已销毁的视图上操作)。


🔄 LifecycleScope

LifecycleScopelifecycle-runtime-ktx 库为每个 LifecycleOwner 提供的 预定义协程作用域。它是一个 CoroutineScope,在该作用域内启动的协程会在 LifecycleOwner 销毁时自动取消,从而避免内存泄漏。

  • 对于 Activity:lifecycleScope 在 Activity 的 onDestroy 时取消。
  • 对于 Fragment:lifecycleScope 在 Fragment 的 onDestroy 时取消(与视图无关)。
  • 对于 Fragment 的视图:viewLifecycleOwner.lifecycleScope 在视图销毁时取消。

使用示例:

lifecycleScope.launch {
    // 这个协程会在 LifecycleOwner 销毁时自动取消
}

lifecycleScope 默认使用 Dispatchers.Main.immediate 作为调度器,适合执行 UI 相关操作。


📦 ViewModelScope

ViewModelScopelifecycle-viewmodel-ktx 库为每个 ViewModel 提供的预定义协程作用域。在 viewModelScope 中启动的协程会在 ViewModel 清除时(即对应的 LifecycleOwner 永久销毁时)自动取消。

ViewModel 本身与生命周期无关,但它的清除时机与 LifecycleOwner 的销毁一致(通常是在 Activity 或 Fragment 最终销毁时)。因此,viewModelScope 适合执行需要在 ViewModel 生命周期内完成的任务,例如网络请求、数据库操作等。

使用示例:

class MyViewModel : ViewModel() {
    fun loadData() {
        viewModelScope.launch {
            val result = repository.fetchData()
            _uiState.value = result
        }
    }
}

viewModelScope 默认使用 Dispatchers.Main,但你可以通过 launch(Dispatchers.IO) 等切换线程。

lifecycleScope 的区别

  • lifecycleScope 绑定到 LifecycleOwner(如 Activity/Fragment),主要用于 UI 层操作。
  • viewModelScope 绑定到 ViewModel,用于业务逻辑,不受配置变更的影响。

🔁 repeatOnLifecycle

repeatOnLifecycle 是一个挂起函数,位于 lifecycle-runtime-ktx 中,用于在 LifecycleOwner 进入指定状态时启动一个协程块,并在离开该状态时自动取消它。当再次进入该状态时,会重新启动一个新的协程块。这解决了以往使用 launchWhenX(如 launchWhenStarted)时协程被挂起但未取消的问题。

为什么需要它?
launchWhenStarted 等函数在生命周期离开时只是挂起协程,协程本身并未取消,这意味着它可能仍持有资源(如数据库监听、网络请求),造成浪费。而 repeatOnLifecycle 在离开时彻底取消协程块,确保资源释放。

典型用法:在 Fragment 中收集 Flow,确保只在界面可见时更新 UI。

class MyFragment : Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        viewLifecycleOwner.lifecycleScope.launch {
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                // 当生命周期 >= STARTED 时,执行此代码块
                // 当生命周期 < STARTED 时,此代码块被取消
                viewModel.uiState.collect { state ->
                    // 更新 UI
                }
            }
        }
    }
}

也可以在一个 repeatOnLifecycle 块中启动多个子协程:

repeatOnLifecycle(Lifecycle.State.STARTED) {
    launch { flow1.collect { ... } }
    launch { flow2.collect { ... } }
}

在 Compose 中,可以使用 collectAsStateWithLifecycle(),它内部封装了 repeatOnLifecycle


🧠 关系总结

  • Lifecycle:生命周期状态的持有者,可被观察。
  • LifecycleOwner:提供 Lifecycle 的组件(Activity/Fragment)。
  • ViewLifecycleOwner:Fragment 中专门代表视图生命周期的 LifecycleOwner。
  • LifecycleScope:为 LifecycleOwner 提供的协程作用域,跟随其销毁而取消。
  • ViewModelScope:为 ViewModel 提供的协程作用域,跟随 ViewModel 清除而取消。
  • repeatOnLifecycle:在 LifecycleOwner 上安全启动和取消协程块的 API,是生命周期感知协程的推荐方式。

它们共同构成了 Android 生命周期与协程结合的完整生态,帮助我们编写更安全、更高效的代码。在实际开发中,请牢记:

  • 在 Fragment 中使用 viewLifecycleOwner 处理 UI 相关操作。
  • 在 ViewModel 中使用 viewModelScope 处理业务逻辑。
  • 在 UI 层收集 Flow 时使用 repeatOnLifecycle 替代旧的 launchWhenX

理解这些组件的关系,是掌握现代 Android 开发的基础之一。