You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

  1. 在两个导航图中,跳转至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>
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 20:21:21