使用hiltNavGraphViewModels时跨导航图复用Fragment的ViewModel状态异常问题
解决方案
问题根源
hiltNavGraphViewModels 要求目标导航图必须存在于当前NavController的回退栈中。当你从nav_home导航进入CourseDetailFragment时,只有nav_home的导航图在栈内,nav_video不在,因此获取categoryViewModel会触发异常;反之从nav_video进入时,nav_home不在栈内,获取homeViewModel也会报错。
可行方案
方案1:根据当前导航栈动态获取ViewModel
通过检查NavController的回退栈,判断当前所属导航图,仅初始化对应作用域的ViewModel:
@AndroidEntryPoint class CourseDetailFragment : Fragment(R.layout.fragment_course_detail), LoadingDelegate by LoadingDelegateImpl(), NavigationDelegate by NavigationDelegateImpl() { private val binding by viewBinding(FragmentCourseDetailBinding::bind) private val viewModel: CourseDetailViewModel by viewModels() private val navController by lazy { findNavController() } // 延迟初始化,仅在对应导航图存在时创建 private val homeViewModel: HomeViewModel? by lazy { runCatching { navController.getBackStackEntry(R.id.nav_home) } .getOrNull()?.let { entry -> ViewModelProvider(entry, defaultViewModelProviderFactory)[HomeViewModel::class.java] } } private val categoryViewModel: CategoryViewModel? by lazy { runCatching { navController.getBackStackEntry(R.id.nav_video) } .getOrNull()?.let { entry -> ViewModelProvider(entry, defaultViewModelProviderFactory)[CategoryViewModel::class.java] } } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 根据存在的ViewModel执行对应逻辑 homeViewModel?.let { vm -> vm.someHomeData.observe(viewLifecycleOwner) { /* 处理home导航图的业务逻辑 */ } } categoryViewModel?.let { vm -> vm.someVideoData.observe(viewLifecycleOwner) { /* 处理video导航图的业务逻辑 */ } } } }
方案2:通过导航参数标记来源,动态初始化ViewModel
- 在两个导航图中,跳转至
CourseDetailFragment时传递来源标识:
<!-- nav_home导航图中的action --> <action android:id="@+id/action_home_to_courseDetail" app:destination="@id/courseDetailFragment"> <argument android:name="from_nav" app:argType="string" android:defaultValue="home"/> </action> <!-- nav_video导航图中的action --> <action android:id="@+id/action_video_to_courseDetail" app:destination="@id/courseDetailFragment"> <argument android:name="from_nav" app:argType="string" android:defaultValue="video"/> </action>
- 在Fragment中根据参数获取对应ViewModel:
@AndroidEntryPoint class CourseDetailFragment : Fragment(R.layout.fragment_course_detail), LoadingDelegate by LoadingDelegateImpl(), NavigationDelegate by NavigationDelegateImpl() { private val binding by viewBinding(FragmentCourseDetailBinding::bind) private val viewModel: CourseDetailViewModel by viewModels() private val fromNav by navArgs<String>("from_nav") // 根据来源动态获取对应作用域的ViewModel private val targetViewModel by lazy { when (fromNav) { "home" -> hiltNavGraphViewModels<R.id.nav_home, HomeViewModel>() "video" -> hiltNavGraphViewModels<R.id.nav_video, CategoryViewModel>() else -> throw IllegalArgumentException("无效的导航来源") } } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) when (targetViewModel) { is HomeViewModel -> { (targetViewModel as HomeViewModel).homeData.observe(viewLifecycleOwner) { // 处理home导航相关逻辑 } } is CategoryViewModel -> { (targetViewModel as CategoryViewModel).categoryData.observe(viewLifecycleOwner) { // 处理video导航相关逻辑 } } } } }
内容的提问来源于stack exchange,提问作者abalta
相关产品推荐
相关产品推荐

