3. ViewModel的生命周期与作用域:onCleared()触发时机、与Activity/Fragment生命周期对比

原理概述

ViewModel 的生命周期与它的宿主(ViewModelStoreOwner)密切相关。默认情况下,ViewModel 的作用域是 Activity 或 Fragment,但它不会因为配置变更(如屏幕旋转)而被销毁。只有当宿主真正结束其生命周期(如 Activity finish(),且非重建场景)时,系统才会调用 ViewModel 的 onCleared() 方法。理解这一机制是避免内存泄漏和正确管理资源的关键。

核心观点: ViewModel 的生命周期独立于 Activity 的配置变更,这使得 UI 数据能够在屏幕旋转等操作后无缝保留。
ViewModel 与 Activity 生命周期对比 Activity 创建 (onCreate) Activity 运行 (onStart/Resume) 配置变更 (onDestroy) Activity 销毁 (onDestroy) ViewModel 创建 ViewModel 活跃 ViewModel 保留 onCleared() 调用 配置变更后保留 最终销毁时清理 核心要点:ViewModel 在配置变更后自动保留在内存中。 onCleared() 仅在 Activity 最终销毁(非配置变更)时由系统自动调用。

3.1 核心API与生命周期集成

在 Activity 或 Fragment 中,通过 ViewModelProvider 获取 ViewModel 实例。系统内部通过 ViewModelStore 维护 ViewModel 的生命周期。当 ViewModelStoreOwner 发生配置变更时,旧的 Activity 会将其 ViewModelStore 传递给新的 Activity,从而实现 ViewModel 的对象保留。

class MyViewModel : ViewModel() {
    override fun onCleared() {
        super.onCleared()
        // 清理协程作业、关闭数据库连接、释放资源
        Log.d("MyViewModel", "onCleared invoked")
    }
}

// 在 Activity 中使用
class MyActivity : AppCompatActivity() {
    private lateinit var viewModel: MyViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        viewModel = ViewModelProvider(this)[MyViewModel::class.java]
        // 此时 ViewModel 被创建,并与 Activity 的生命周期绑定
    }
}

3.2 onCleared() 触发时机详解

理解 onCleared() 的触发时机,是区分 ViewModel 生命周期与 Activity/Fragment 生命周期的关键。以下是几种典型场景:

  • 场景 A:Activity 正常完成(非配置变更)
    当 Activity 调用 finish() 或被系统正常销毁且不会重建时,系统会清除 ViewModelStore,并触发 ViewModel 的 onCleared() 回调。这是最常见的资源清理时机。
  • 场景 B:Fragment 被分离或完成
    当 Fragment 被移除或所在的 Activity 最终销毁时,其对应的 ViewModelStore 被清除,onCleared() 被调用。
  • 场景 C:进程被系统杀死
    当进程被系统回收时,onCleared() 可能不会被调用。此时应用应依赖 SavedStateHandle 来持久化关键数据,以便后续恢复。
最佳实践: 使用 Kotlin 协程时,优先利用 viewModelScope 进行协程管理。viewModelScope 会在 onCleared() 被调用时自动取消其内部的协程作业,从而有效避免协程泄漏。

3.3 生命周期对比:原理与差异

为了更清晰地展示 ViewModel 与 Activity/Fragment 生命周期的差异,下表从多个维度进行了对比:

对比维度 Activity / Fragment ViewModel
作用域 依赖于用户导航和系统配置 依赖于 ViewModelStoreOwner 的生命周期
创建时机 onCreate() / onAttach() 当 ViewModelProvider 被首次调用时
配置变更 销毁并重建 自动保留在内存中
销毁时机 onDestroy() onCleared() (当 Owner 最终销毁时)
主要职责 管理 UI 生命周期、显示数据 管理 UI 数据、业务逻辑

3.4 常见错误与稳定性优化

常见陷阱: 不要在 ViewModel 中持有 Activity、Fragment 或 View 的强引用。由于 ViewModel 的生命周期比单个 Activity 实例长,强引用 View 对象会导致 Activity 无法被垃圾回收,从而造成严重的内存泄漏。如果需要 Context,请使用 AndroidViewModel(application) 或 SavedStateHandle。

为了提升应用的稳定性和性能,在实际开发中应遵循以下优化策略:

  1. 及时清理外部资源
    在 onCleared() 回调中,主动取消网络请求、关闭数据库连接、解绑系统服务或释放 FileDescriptor。不要依赖系统垃圾回收。
  2. 避免存储大对象
    不要在 ViewModel 中存储大型 Bitmap、视图层级结构等对象。如果必须持有,请在 onCleared() 中将其置为 null,以便 GC 及时回收。
  3. 使用 Lifecycle-aware 组件
    结合 LiveData、StateFlow 等响应式组件,确保数据流能够自动感知生命周期状态,避免在后台更新 UI 导致崩溃。