Android ViewModel无法从BackStackEntry获取选中项如何解决
问题根因
- 你两处获取的
MapViewModel作用域不一致,本质是两个完全独立的实例,所以数据无法共享:- ListFragment中使用
ViewModelProviders.of(this).get(MapViewModel.class),得到的是和ListFragment自身生命周期绑定的ViewModel实例 - MapFragment中使用回退栈条目
backStackEntry作为ViewModelStoreOwner,得到的是和fishListFragment回退栈条目绑定的ViewModel实例
两个实例数据互不相通,自然收不到回调日志。
- ListFragment中使用
修复方案
1. 统一ListFragment的ViewModel获取方式
把ListFragment里的ViewModel初始化逻辑改成和MapFragment同作用域,确保拿到的是同一个实例:
// 替换原有的ViewModelProviders.of(this)代码 NavController navController = Navigation.findNavController(requireView()); NavBackStackEntry backStackEntry = navController.getBackStackEntry(R.id.fishListFragment); mMapViewModel = new ViewModelProvider(backStackEntry).get(MapViewModel.class); // 后续点击事件里的select逻辑不变 Log.d("view-click", String.valueOf(fish.getLocation())); mMapViewModel.select(fish); Navigation.findNavController(view).navigate(R.id.action_fishListFragment_to_mapFragment2);
修改后两边操作的是同一个ViewModel,你存在里面的Fish数据就能被MapFragment正常接收。
2. 兼容底部导航直接进入MapFragment的场景
如果用户通过底部导航直接进入MapFragment,此时回退栈不存在fishListFragment条目,调用getBackStackEntry会抛出异常,建议加判断做兼容,同时优化观察者的生命周期绑定避免内存泄漏:
NavController navController = NavHostFragment.findNavController(this); // 先判断是否是从ListFragment跳转过来的 NavBackStackEntry previousEntry = navController.getPreviousBackStackEntry(); if (previousEntry != null && previousEntry.getDestination().getId() == R.id.fishListFragment) { NavBackStackEntry backStackEntry = navController.getBackStackEntry(R.id.fishListFragment); mMapViewModel = new ViewModelProvider(backStackEntry).get(MapViewModel.class); // 用Fragment的view生命周期作为观察者所有者,避免内存泄漏 mMapViewModel.getSelected().observe(getViewLifecycleOwner(), fish -> { if (fish != null) { Log.d("MAPFRAGMENT",fish.getLocation() + ""); // 处理完后清空选中值,避免回退后再次进入重复触发回调 mMapViewModel.select(null); } }); } else { // 底部导航直接进入的逻辑,展示默认位置即可 }
3. 可选优化
- 废弃的
ViewModelProviders.of写法可以全部替换成ViewModelProvider构造写法,保持代码风格统一 - 如果后续还要支持更多页面共享该ViewModel,可以直接把ViewModel的作用域绑定到宿主Activity,就不用处理回退栈的判断逻辑。
内容的提问来源于stack exchange,提问作者AndroidJunior
相关产品推荐
相关产品推荐

