理解 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 协作基本原则

原则说明
数据驱动 UIFragment 观察 ViewModel 的数据变化,ViewModel 不持有 Fragment 引用。
生命周期平衡Fragment 在 onDestroyView 中清理 UI 相关引用,ViewModel 在 onCleared 中释放长期资源。
作用域绑定多个 Fragment 可共享同一 Activity 作用域的 ViewModel,但不要跨 Fragment 传递 Context 给 ViewModel。
状态还原使用 SavedStateHandle 在进程杀死时恢复部分关键状态,避免暴力依赖 ViewModel 的持久化。

1.4 生命周期范围对比

ViewModel 与 Fragment 生命周期范围对比 Fragment 生命周期 onCreate → onCreateView → onViewCreated → onStart → onResume → ... → onPause → onStop → onDestroyView → onDestroy ViewModel 生命周期 创建:首次请求 ViewModel(通常在 Fragment.onCreate 中) 清除:宿主 Fragment 彻底销毁(Activity finished 或 Fragment detached) 配置变更 (如屏幕旋转) Fragment 被重建, ViewModel 保持相同实例 ViewModel 作用域 通常对应宿主 Fragment

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} 已清除")
}