使用Navigation+Hilt时,如何在Tab子Fragment中正确实例化父导航作用域ViewModel?
问题:子Fragment绑定父导航作用域ViewModel时抛出异常
我正在使用Navigation Component + Hilt依赖注入。有一个作为导航目的地的Fragment,它承载着包含2个固定Fragment的ViewPager。我想在这些子Fragment中实例化ViewModel,并将其作用域限定到父导航目的地,目前使用的代码如下:
private val vm: ChildViewModel by hiltNavGraphViewModels(R.id.parentFragment) private val cvm: ParentViewModel by hiltNavGraphViewModels(R.id.parentFragment)
但这段代码有时会抛出IllegalArgumentException异常,异常信息为:
java.lang.IllegalArgumentException: No destination with ID 2131363082 is on the NavController's back stack. The current destination is Destination(...)
解决方法
1. 核心原因分析
异常本质是调用hiltNavGraphViewModels时,指定的父导航目的地ID不在当前NavController的回退栈中。通常是因为子Fragment初始化时机过早(父Fragment还未完成导航入栈),或者父Fragment已被弹出回退栈但子Fragment仍尝试获取ViewModel。
2. 推荐方案:通过父Fragment间接传递ViewModel
避免子Fragment直接依赖NavGraph获取,改为在父Fragment中初始化共享ViewModel,再传递给子Fragment:
- 父Fragment代码:
// 父Fragment中初始化作用域到导航目的地的ViewModel private val parentVm: ParentViewModel by hiltNavGraphViewModels(R.id.parentFragment) private val childVm: ChildViewModel by hiltNavGraphViewModels(R.id.parentFragment) // 创建ViewPager适配器时传递ViewModel class ViewPagerAdapter( fragment: Fragment, private val parentVm: ParentViewModel, private val childVm: ChildViewModel ) : FragmentStateAdapter(fragment) { override fun createFragment(position: Int): Fragment { return when(position) { 0 -> ChildFragment1().apply { arguments = bundleOf( "parent_vm" to parentVm, "child_vm" to childVm ) } 1 -> ChildFragment2().apply { arguments = bundleOf( "parent_vm" to parentVm, "child_vm" to childVm ) } else -> throw IllegalArgumentException("无效的页面位置") } } override fun getItemCount() = 2 }
- 子Fragment代码:
class ChildFragment1 : Fragment() { private lateinit var parentVm: ParentViewModel private lateinit var childVm: ChildViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) arguments?.let { parentVm = it.get("parent_vm") as ParentViewModel childVm = it.get("child_vm") as ChildViewModel } } }
3. 备选方案:延迟ViewModel初始化
如果坚持使用hiltNavGraphViewModels,可以通过懒加载+延迟使用的方式规避时机问题:
private val vm: ChildViewModel by lazy { hiltNavGraphViewModels(R.id.parentFragment).value } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 在此处才开始使用vm,确保父导航目的地已入栈 }
注意:这种方式仍存在不确定性,不如父Fragment传递可靠。
4. 基础检查
确认导航图中R.id.parentFragment对应的目的地ID配置正确,没有拼写错误或资源混淆导致的ID不匹配问题。
内容的提问来源于stack exchange,提问作者rdias002
相关产品推荐
相关产品推荐

