2. ViewModel 核心原理回顾

ViewModel 是 Android Jetpack 架构组件中最关键的部分之一,专门用于以生命周期感知的方式存储和管理界面相关的数据。它能够在 Activity 或 Fragment 因配置变更(如屏幕旋转)而重建时保持数据存活,避免重复加载网络、数据库或计算任务。本章从生命周期、作用域以及与 Activity / Fragment 的绑定关系三个维度展开,并为后续与 DataStore 的协同打下理论基础。

核心概念提炼: ViewModel 的生命周期 长于 宿主(Activity / Fragment),作用域随宿主自然消亡,其内部数据不会因配置变更而丢失。

2.1 ViewModel 的生命周期

  • 创建时机: 当 Activity / Fragment 首次调用 ViewModelProvider(model).get(XXViewModel::class.java) 时创建。
  • 存活边界: ViewModel 一直存活,直到其关联的 Activity 真正 finish 或 Fragment 脱离 FragmentManager(即宿主生命周期最终销毁)。
  • 配置变更: 旋转屏幕、切换语言、改变字体大小等暂时销毁并重建 Activity,但 ViewModel 不会销毁,新 Activity 会拿到同一个 ViewModel 实例。
  • 主动清除: 调用 ViewModelStore.clear() 或宿主 finish 时,ViewModel 的 onCleared() 回调会被触发,释放资源。
宿主状态 ViewModel 状态 数据是否保留 典型场景
Activity onCreate(首次) 创建 — 首次初始化数据
Activity 旋转重建 保持存活 是 列表滚动位置、表单输入
Activity 被系统回收 可能被销毁(依赖 SavedState) 部分保留(SavedStateHandle) 低内存 kill 后重建
Activity finish() 销毁(onCleared) 否 用户退出页面

2.2 ViewModel 的作用域

作用域决定了 ViewModel 的共享范围和生命周期边界。Android 提供了三种常用作用域,且可通过自定义 ViewModelStoreOwner 扩展。

作用域 生命周期绑定点 典型使用 备注
Activity Activity 生命周期 该 Activity 下所有 Fragment 共享 最常用
Fragment Fragment 生命周期 仅在单个 Fragment 内有效 默认作用域
NavBackStackEntry Navigation 图的回退栈 导航图中特定目的地的状态 需搭配 Navigation Compose / Fragment
作用域选择建议: 如果需要在 Activity 内的多个 Fragment 之间共享数据(如登录状态、购物车),使用 Activity 作用域;数据仅属于当前 Fragment 时,使用 Fragment 作用域,避免不必要的耦合。

2.3 ViewModel 与 Activity / Fragment 的关系

ViewModel 通过 ViewModelStore 与宿主绑定。每个 ViewModelStoreOwner(Activity/Fragment)持有一个 ViewModelStore,当宿主被销毁时, ViewModelStore 被清空,所有关联的 ViewModel 执行 onCleared()。关键 API 与实现流程如下:

  1. 获取实例: ViewModelProvider(this).get(MyViewModel::class.java),参数 this 即为 ViewModelStoreOwner。
  2. 构造注入: 使用 ViewModelProvider.Factory 传入自定义参数或依赖(如 DataStore)。
  3. 生命周期回调: ViewModel 内部不应持有直接引用 Activity / Fragment 的 Context 或 View,以避免内存泄漏。需要 Context 时通过 Application 作用域或 SavedStateHandle 获取。
  4. 数据暴露: 通常使用 LiveData 或 StateFlow 对外暴露数据,确保 UI 层观察者随生命周期自动取消注册。

2.4 核心流程图:ViewModel 生命周期与宿主关系

ViewModel 生命周期与宿主关系图 ViewModel 生命周期与宿主关系 Activity onCreate 配置变更(旋转) onDestroy → onStart Activity finish() Activity 真正销毁 ViewModel 存活区 配置变更时保持 数据不丢失 onCleared() 释放资源 深色框代表 Activity 生命周期阶段,绿色区域代表 ViewModel 存活窗口

2.5 关键 API 与代码实现

以下示例展示了如何在 Activity 中获取 ViewModel 并观察其数据:

class MainActivity : AppCompatActivity() {
    private lateinit var viewModel: MyViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 通过 ViewModelProvider 获取 ViewModel 实例
        viewModel = ViewModelProvider(this).get(MyViewModel::class.java)

        // 观察 LiveData(生命周期感知)
        viewModel.uiState.observe(this) { state ->
            // 更新 UI
        }
    }
}

自定义 Factory 注入 DataStore:

class MyViewModelFactory(
    private val dataStore: DataStore<Preferences>
) : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        return MyViewModel(dataStore) as T
    }
}

// 使用时
val factory = MyViewModelFactory(dataStore)
val vm = ViewModelProvider(this, factory).get(MyViewModel::class.java)
常见错误: 在 ViewModel 中保存 Activity / Fragment 的 Context 或 View 引用会导致内存泄漏。如需 Context,请使用 ViewModelProvider.Factory 传入 Application 或使用 SavedStateHandle。

2.6 调试与性能优化要点

  • 日志跟踪: 在 init{} 和 onCleared() 中添加 Log.d,确认是否异常重建或多次实例化。
  • 避免大对象: ViewModel 内部不要持有大量 bitmap 或大集合,否则配置变更时仍占用内存,增加 OOM 风险。超过 50KB 的数据建议使用 Room 或 DataStore 持久化。
  • 协程作用域: 推荐使用 viewModelScope(Ktx 扩展)管理协程,当 ViewModel 清除时自动取消,避免泄露。
  • SavedStateHandle: 即使系统进程杀死 Activity,也能恢复少量关键数据,配合 ViewModel 使用可提升稳定性。
// 使用 viewModelScope 发起协程
viewModelScope.launch {
    dataStore.data.collect { preferences ->
        // 更新状态
    }
}

2.7 稳定性优化

问题 原因 解决方案
ViewModel 重复创建 多次调用 get() 但未使用同一个 ViewModelStoreOwner 确保宿主对象一致(如都传入 this)
配置变更后数据不更新 LiveData 未正确设置观察者或 Activity 重建后未重新订阅 使用 LifecycleOwner 参数 this 自动管理观察
内存泄漏 ViewModel 持有 Activity 引用 仅使用 Application Context 或 SavedStateHandle
协程任务泄漏 未使用 viewModelScope 统一用 viewModelScope 启动协程
本章小结: ViewModel 的生命周期长于宿主,通过 ViewModelStore 绑定,配置变更时不销毁;其作用域可选择 Activity、Fragment 或 Navigation 回退栈;与宿主通信时使用 LiveData / StateFlow,避免直接引用 UI 组件。掌握这些原理是后续实现 ViewModel + DataStore 协同的基石。