理解 ViewModel 本质与 Fragment 职责边界
ViewModel 是 Jetpack 架构组件中用于管理和存储 UI 相关数据的核心类。其设计目标是:在配置变更(如屏幕旋转)后自动保留数据,从而减少重复加载和状态丢失。Fragment 持有独立的生命周期,但 Fragment 的重建频率远高于 ViewModel。理解两者的边界,是构建稳定可维护应用的前提。
1.1 ViewModel 设计目标
- 生命周期感知:ViewModel 自动与作用域(Activity/Fragment)关联,在作用域结束时才清除。
- 数据持久化:配置变更时不销毁,直接复用旧实例中的数据。
- 异步解耦:避免在 Fragment 中直接操作异步任务导致内存泄漏,ViewModel 通过
viewModelScope管理协程。 - 单向数据流:ViewModel 持有
StateFlow/LiveData,Fragment 仅观察,不直接修改。
1.2 Fragment 生命周期约束
Fragment 的生命周期以其宿主(Activity 或宿主 Fragment)的生命周期为基准,但额外拥有 onCreateView / onViewCreated / onDestroyView 等阶段。在 Fragment 重建时,onCreate 先于 onCreateView 调用。获取 ViewModel 的最佳时机是在 onCreate 或 onViewCreated 中,但必须确保 Fragment 已与 Activity 关联。
⚠ 常见错误:直接在 Fragment 的构造方法中创建 ViewModel 会导致内存泄漏,因为构造时 Fragment 尚未初始化生命周期。务必使用
ViewModelProvider 或委托 by viewModels()。
1.3 协作基本原则
| 原则 | 说明 |
|---|---|
| 数据驱动 UI | Fragment 观察 ViewModel 的数据变化,ViewModel 不持有 Fragment 引用。 |
| 生命周期平衡 | Fragment 在 onDestroyView 中清理 UI 相关引用,ViewModel 在 onCleared 中释放长期资源。 |
| 作用域绑定 | 多个 Fragment 可共享同一 Activity 作用域的 ViewModel,但不要跨 Fragment 传递 Context 给 ViewModel。 |
| 状态还原 | 使用 SavedStateHandle 在进程杀死时恢复部分关键状态,避免暴力依赖 ViewModel 的持久化。 |
1.4 生命周期范围对比
1.5 Fragment 中获取 ViewModel(代码示例)
// 方式一:使用委托扩展(推荐)
class UserFragment : Fragment() {
private val viewModel: UserViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// 开始观察数据
viewModel.user.observe(viewLifecycleOwner) { user ->
// 更新 UI
}
}
}
// 方式二:使用 ViewModelProvider(适用于自定义工厂)
private lateinit var viewModel: UserViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 在 onCreate 中初始化,保证生命周期绑定正确
viewModel = ViewModelProvider(this)[UserViewModel::class.java]
}
// 注意:使用 SharedViewModel(Activity 作用域)
private val sharedViewModel: SharedViewModel by viewModels(
ownerProducer = { requireActivity() }
)
✨ 最佳实践:
- 始终使用
by viewModels()或lazy延迟初始化,避免在构造中创建。 - Fragment 中观察 LiveData/Flow 时必须传入
viewLifecycleOwner,而非this,防止重复回调。 - ViewModel 不应持有
View、Activity或Fragment的强引用——使用SavedStateHandle或Application级别上下文(通过AndroidViewModel)。 - 在
onCleared()中取消仍在进行的异步任务(使用viewModelScope自动管理则无需手动处理)。
1.6 性能与稳定性要点
- 避免在 ViewModel 中存储大型 bitmap 或集合;使用
SavedStateHandle仅保存必要序列化数据。 - 配置变更后 Fragment 重建时,ViewModel 不会重新创建,但
onCreateView会被调用,需在onViewCreated中重新绑定观察。 - 避免多个 Fragment 共享同一个 ViewModel 时产生数据竞争:推荐使用不可变状态,并通过
StateFlow的value安全更新。
调试技巧: 在 ViewModel 的
onCleared() 中添加日志输出,可以直观验证 ViewModel 生命周期是否正确。例如:override fun onCleared() {
super.onCleared()
Log.d("ViewModelDebug", "${this::class.simpleName} 已清除")
}