Fragment中onCreate与onViewCreated获取ViewModel的差异:崩溃原因解析及最佳实践疑问
问题描述
我有一个包含Fragment(命名为MyFragment)的Android Activity,在MyFragment的onCreate()方法中,我通过如下代码获取ViewModel:
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); myViewModel = new ViewModelProvider(requireActivity()).get(MyViewModel.class); }
崩溃现象
有时将应用置于后台并切换至其他应用后,再次将该应用带回前台时会发生崩溃,报错信息如下:
Caused by: java.lang.RuntimeException: Cannot create an instance of class com.example.MyViewModel ... at com.example.MyFragment.onCreate(MyFragment.java:64)
其中第64行正是上述获取ViewModel的代码。
查阅ViewModel官方文档后,我发现所有Fragment相关的代码示例均在onViewCreated()方法中获取ViewModel,而非onCreate()。将代码修改为在onViewCreated()中获取ViewModel后,上述崩溃问题不再出现。
疑问
- 为何在Fragment的
onCreate()中获取ViewModel会导致应用崩溃,而在onViewCreated()中获取可解决该问题? - 是否应始终遵循官方文档示例,在Fragment的
onViewCreated()而非onCreate()中获取ViewModel?
回答
1. 崩溃原因解析
崩溃的核心本质是Fragment的onCreate()阶段,Activity或Fragment的状态还未完全稳定,导致ViewModel的实例化条件不满足,具体可以拆解为几个常见场景:
ViewModel构造依赖未就绪:如果你的
MyViewModel包含带参数的构造函数(比如依赖Activity的Context、Repository实例、或者Activity中初始化的其他组件),而这些依赖项是在Activity的onCreate()后期、onStart()甚至更晚的阶段才完成初始化的,那么在Fragment的onCreate()中尝试创建ViewModel时,这些依赖还没准备好,自然会抛出实例化失败的异常。而onViewCreated()是在Fragment的View创建完成后调用的,此时Activity已经完成了核心初始化工作,所有依赖项都处于就绪状态。Activity的ViewModelStore恢复时序问题:当应用被系统后台回收后重建时,系统会尝试恢复Activity的
ViewModelStore(用于保存ViewModel的状态),但这个恢复过程可能在Fragment的onCreate()之后才完成。此时用requireActivity()作为ViewModelStoreOwner获取ViewModel,ViewModelProvider无法正确访问到恢复后的存储容器,或者无法通过SavedStateHandle恢复ViewModel的状态,最终导致实例化失败。而onViewCreated()阶段,Activity的ViewModelStore已经完全恢复,状态稳定,能够正常支撑ViewModel的创建。生命周期同步的细微差异:虽然Fragment的
onCreate()是在Activity的onCreate()之后调用,但此时Activity可能还没完成所有初始化步骤(比如Intent参数的解析、全局服务的绑定等)。如果ViewModel的创建依赖这些未完成的步骤,就会触发异常。而onViewCreated()发生在Activity的onStart()之前,但此时Activity的核心初始化已经完成,足以支撑ViewModel的实例化。
2. 是否必须遵循官方推荐的时机?
推荐优先遵循官方文档的实践,在onViewCreated()中获取ViewModel,但也存在少数例外情况:
优先选择
onViewCreated()的场景:绝大多数情况下,ViewModel需要和Fragment的View交互(比如观察LiveData更新UI、绑定点击事件等),onViewCreated()是最佳时机——此时View已经存在,不会出现NullPointerException,同时Activity和Fragment的状态都稳定,能避免大部分生命周期同步问题。而且统一在这个时机获取ViewModel,能让代码风格更一致,降低维护成本。可以在
onCreate()中获取的例外场景:如果你的ViewModel完全不涉及View交互,只是用于处理独立的业务逻辑(比如提前预加载数据、管理后台任务),并且其构造函数不依赖任何Activity后期初始化的资源,那么在onCreate()中获取也可行。但即使这种情况,依然建议尽量遵循官方规范,避免潜在的生命周期坑。
另外补充:如果确实需要在onCreate()中获取ViewModel,一定要确保ViewModel有正确的无参构造函数,或者提供自定义的ViewModelProvider.Factory,并且保证Factory依赖的所有资源在FragmentonCreate()时已经就绪。但这会增加代码复杂度,远不如直接用官方推荐的方式稳妥。
内容的提问来源于stack exchange,提问作者Thanasis M

