第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 管理实例:
2.5 常见错误与调试方法
错误1:在 Fragment 构造方法或 onCreateView 前使用 ViewModelProvider
new ViewModelProvider(this) 要求 Fragment 已附加到 Activity。若在 onAttach() 之前调用,会导致 IllegalStateException。解决:将初始化放在 onCreate() 或 onViewCreated() 中。
错误2:混合使用 Fragment 和 Activity 的 ViewModelStore
若在 Fragment A 中使用
若在 Fragment A 中使用
requireActivity() 获取 ViewModel,Fragment B 也使用相同 Activity 的 ViewModelStore,但误传了不同的 key,会导致两个不同实例。始终确保传同一 ViewModelStoreOwner。
调试技巧:通过
ViewModelStore.clear() 日志或断点观察 onCleared() 是否被调用,可验证生命周期绑定是否正确。也可以使用 Fragment.getViewModelStore() 打印缓存大小。
2.6 实现步骤总结
- 在 Fragment 的
onCreate()或onViewCreated()中调用new ViewModelProvider(owner)。 - 决定
owner:单个 Fragment 用this;跨 Fragment 共享用requireActivity()。 - 通过
.get(ViewModelClass)获取实例,不要调用new。 - 如果需要自定义 Factory(传参),则使用
new ViewModelProvider(owner, factory)。 - 确认 ViewModel 的
onCleared()在 Fragment 销毁后触发,避免内存泄漏。
2.7 性能与稳定性考量
- 避免在 onCreateView 中重复创建 ViewModelProvider:只需在 Fragment 的
onCreate中初始化一次。 - 自定义 Factory 不要持有 Activity/Fragment 引用:使用
SavedStateHandle或Application级别依赖。 - 大内存 ViewModel:若 ViewModel 包含大量 Bitmap 或 List,确保
onCleared()中释放资源。
重要:永远不要将 ViewModel 的引用保存在静态变量中,这会导致 ViewModel 无法被垃圾回收,生命周期管理失效。