第2章:掌握 ViewModelProvider 的正确初始化方式

ViewModel 实例不能通过 new 关键字直接创建。直接依赖构造函数注入会绕过 Android 生命周期管理,导致配置变更(如屏幕旋转)时数据丢失或内存泄漏。Android 提供了 ViewModelProvider 作为标准工厂,自动处理作用域、缓存与生命周期绑定。本章聚焦于如何通过 Fragment 获取正确作用域的 ViewModel,并解释 ViewModelProvider 的内部机制。

2.1 为什么不能直接 new ViewModel

方案 生命周期感知 配置变更存活 内存泄漏风险
直接 new MyViewModel() 否 否 高
使用 ViewModelProvider 是 是 低

直接创建实例时,ViewModel 无法与 Fragment 的 LifecycleOwner 关联,当 Fragment 销毁重建时,旧实例被垃圾回收,新 Fragment 会创建全新的 ViewModel,导致视图数据丢失。同时,若 ViewModel 持有 Activity 引用或 Context,配置变更后旧 ViewModel 未被清除,可能泄漏。

2.2 ViewModelProvider 初始化模式

ViewModelProvider 提供两种核心构造方式:

  • 基于 Fragment/Activity 实例:new ViewModelProvider(fragment)——作用域为 Fragment 自身。
  • 基于 ViewModelStoreOwner:new ViewModelProvider(fragment.getViewModelStore())——通常用于自定义作用域。

标准用法如下:

public class MyFragment extends Fragment {
    private MyViewModel viewModel;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // ✅ 正确:通过 Fragment 实例获取作用域
        viewModel = new ViewModelProvider(this).get(MyViewModel.class);
    }
}

2.3 作用域选择的关键:Fragment vs Activity

获取 ViewModel 时传入不同的 ViewModelStoreOwner 决定了 ViewModel 的生命周期范围:

传入对象 作用域 数据共享范围 常见场景
this(Fragment 自身) Fragment 生命周期 仅该 Fragment 独立 UI 状态
requireActivity() 宿主 Activity 生命周期 Activity 内所有 Fragment 跨 Fragment 通信
getParentFragment() 父 Fragment 生命周期 父子 Fragment 嵌套 Fragment 共享
常见错误:在 Fragment 中传递 getActivity() 但未使用 requireActivity()——前者可能返回 null,后者会在 Fragment 未附加时抛出 IllegalStateException,更安全。

2.4 ViewModelProvider 内部流程

下图展示了 ViewModelProvider 如何通过 ViewModelStore 管理实例:

ViewModelProvider 初始化流程 Fragment 调用 ViewModelProvider(this) 从 Fragment 获取 ViewModelStore 检查 ViewModelStore 中 是否存在 key 对应实例 不存在:调用 Factory.create() 存在:直接返回 缓存实例 将新实例放入 ViewModelStore 缓存 返回 ViewModel 实例 Fragment 获得可用的 ViewModel

2.5 常见错误与调试方法

错误1:在 Fragment 构造方法或 onCreateView 前使用 ViewModelProvider
new ViewModelProvider(this) 要求 Fragment 已附加到 Activity。若在 onAttach() 之前调用,会导致 IllegalStateException。解决:将初始化放在 onCreate() 或 onViewCreated() 中。
错误2:混合使用 Fragment 和 Activity 的 ViewModelStore
若在 Fragment A 中使用 requireActivity() 获取 ViewModel,Fragment B 也使用相同 Activity 的 ViewModelStore,但误传了不同的 key,会导致两个不同实例。始终确保传同一 ViewModelStoreOwner。
调试技巧:通过 ViewModelStore.clear() 日志或断点观察 onCleared() 是否被调用,可验证生命周期绑定是否正确。也可以使用 Fragment.getViewModelStore() 打印缓存大小。

2.6 实现步骤总结

  1. 在 Fragment 的 onCreate() 或 onViewCreated() 中调用 new ViewModelProvider(owner)。
  2. 决定 owner:单个 Fragment 用 this;跨 Fragment 共享用 requireActivity()。
  3. 通过 .get(ViewModelClass) 获取实例,不要调用 new。
  4. 如果需要自定义 Factory(传参),则使用 new ViewModelProvider(owner, factory)。
  5. 确认 ViewModel 的 onCleared() 在 Fragment 销毁后触发,避免内存泄漏。

2.7 性能与稳定性考量

  • 避免在 onCreateView 中重复创建 ViewModelProvider:只需在 Fragment 的 onCreate 中初始化一次。
  • 自定义 Factory 不要持有 Activity/Fragment 引用:使用 SavedStateHandle 或 Application 级别依赖。
  • 大内存 ViewModel:若 ViewModel 包含大量 Bitmap 或 List,确保 onCleared() 中释放资源。
重要:永远不要将 ViewModel 的引用保存在静态变量中,这会导致 ViewModel 无法被垃圾回收,生命周期管理失效。