请求解决:Activity重启调用Fragment引发ViewModelProvider创建异常
先看你给出的崩溃堆栈,核心错误是java.lang.IllegalStateException: Can't create ViewModelProvider for detached fragment——这说明当你在Activity的onRestart方法里调用Fragment的search方法时,这个Fragment已经和Activity解除绑定(detached)了,这时候调用ViewModelProviders.of(this)就会触发异常,因为ViewModelProvider需要Fragment处于attached状态才能关联到正确的生命周期所有者。
为什么Fragment会detached呢?因为ViewPager默认会销毁当前页面之外一定数量的Fragment(默认是左右各1个),或者当App进入后台后系统回收内存时,也可能把非当前显示的Fragment销毁成detached状态。而Activity的onRestart方法调用时,这些被销毁的Fragment可能还没完成重新attach的流程,直接调用它的方法自然会出问题。
给你几个可行的解决方案:
1. 调用Fragment方法前先检查其attach状态
在Activity的onRestart里,先判断Fragment是否已经附加到Activity,再执行刷新操作:
MyListFragment fragment = (MyListFragment) adapter.getFragmentAtPosition(0); // 先确认Fragment不为null且已附加到Activity if (fragment != null && fragment.isAdded()) { fragment.search(""); }
2. 在Fragment内部先检查自身状态
在Fragment的search方法开头,先判断自己是否处于attached状态,如果不是就直接返回,避免后续创建ViewModelProvider时出错:
public void search(String query) { // 先检查当前Fragment是否已附加到Activity if (!isAdded()) { return; } query = "%" + query + "%"; if (myListViewModel == null) { myListViewModel = ViewModelProviders.of(this, viewModelFactory).get(MyListViewModel.class); } if (myListViewModel.mList != null && myListViewModel.mList.hasActiveObservers()) { myListViewModel.mList.removeObservers(this); } myListViewModel.getFilteredList(query).observe(this, mainObserver); }
3. 改用Activity作为ViewModel的生命周期宿主(可选)
如果你的ViewModel不需要和Fragment的生命周期严格绑定,而是可以和Activity生命周期一致,那么可以把ViewModelProviders.of(this)改成ViewModelProviders.of(getActivity()),这样即使Fragment处于detached状态,也能正常获取ViewModel:
if (myListViewModel == null) { // 改用Activity作为ViewModel的宿主 myListViewModel = ViewModelProviders.of(getActivity(), viewModelFactory).get(MyListViewModel.class); }
不过这个方案要注意:ViewModel的生命周期会和Activity绑定,即使Fragment被销毁,ViewModel也会保留到Activity销毁,适合需要跨Fragment共享数据的场景。
4. 让Fragment自己监听刷新时机
更优雅的方式是不要从Activity主动调用Fragment的方法,而是通过LiveData传递刷新事件。比如在Activity的ViewModel里定义一个LiveData,Fragment在onViewCreated里监听这个LiveData,当Activity需要刷新时,更新LiveData的值,Fragment自动执行刷新:
- Activity侧:
// 在Activity的ViewModel里定义 private MutableLiveData<String> refreshTrigger = new MutableLiveData<>(); // onRestart里触发刷新 refreshTrigger.setValue(""); // 提供给Fragment观察的方法 public LiveData<String> getRefreshTrigger() { return refreshTrigger; }
- Fragment侧:
@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); // 监听Activity的刷新事件 ((HomeActivityViewModel) ViewModelProviders.of(getActivity()).get(HomeActivityViewModel.class)) .getRefreshTrigger() .observe(getViewLifecycleOwner(), query -> { // 执行刷新操作 search(query); }); }
这种方式可以避免直接操作Fragment,完全通过生命周期感知的方式处理,更符合Jetpack组件的设计理念。
内容的提问来源于stack exchange,提问作者Sohail Ahmad Khan

