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

ViewModel注入NavController失败及ViewModel发起导航方案咨询

在ViewModel中注入NavController遇编译错误,如何实现ViewModel发起导航?

原实现代码

ViewModel代码

@HiltViewModel
class DeviceHolderListViewModelImpl @Inject constructor(
    private val fetchUsersUseCase: FetchUsersUseCase,
    private val navigationUtil: NavigationUtil
    ) : DeviceHolderListViewModel, ViewModel() {
      
      // 尝试在此处调用navigationUtil

}
class NavigationUtil @Inject constructor(private val navController: NavController) {

    fun navigateTo(destination: String, bundle: Bundle) {

        when(destination) {
            DEVICE_HOLDER_LIST -> navController.navigate(R.id.action_global_goto_deviceHolderListFragment, bundle)
            DEVICE_HOLDER_DETAILS -> navController.navigate(R.id.action_global_goto_deviceHolderDetailsFragment, bundle)
        }
    }

    fun navigateBack() {
        navController.popBackStack()
    }
}
@Module
@InstallIn(ActivityComponent::class)
object NavigationModule {

    @Provides
    fun provideNavController(activity: AppCompatActivity): NavController {
        return Navigation.findNavController(activity, R.id.nav_host_fragment)
    }

    @Provides
    fun provideNavigationUtil(navController: NavController): NavigationUtil {
        return NavigationUtil(navController)
    }
}

编译错误

error: [Dagger/MissingBinding] androidx.navigation.NavController
cannot be provided without an @Inject constructor or an
@Provides-annotated method.

疑问

  • 是不是因为试图从ViewModel中访问navController,而它应该在Fragment或Activity中访问?
  • 目标是从ViewModel中发起导航,理想的实现方式是什么?

编辑后修改的代码

修改后的NavigationUtil代码

class NavigationUtil {

    private var navController: NavController? = null

    fun setController(controller: NavController) {
        navController = controller
    }

    fun clear() {
        navController = null
    }

    fun navigateTo(destination: String, bundle: Bundle) {

        when(destination) {
            DEVICE_HOLDER_LIST -> navController?.navigate(R.id.action_global_goto_deviceHolderListFragment, bundle)
            DEVICE_HOLDER_DETAILS -> navController?.navigate(R.id.action_global_goto_deviceHolderDetailsFragment, bundle)
        }
    }

    fun navigateBack() {
        navController?.popBackStack()
    }
}

修改后的NavigationModule代码

@Module
@InstallIn(ActivityComponent::class)
object NavigationModule {

    @Provides
    @ViewModelScoped
    fun provideNavigationUtil(): NavigationUtil {
        return NavigationUtil()
    }
}

问题解析与解决方案

错误原因

  1. 作用域不匹配:@HiltViewModel标注的ViewModel属于ViewModelComponent作用域,而你的NavigationModule安装在ActivityComponent中,Dagger无法跨作用域提供依赖,导致NavController无法注入到ViewModel依赖的NavigationUtil中。
  2. 生命周期风险:NavController绑定的是Activity/Fragment的生命周期,ViewModel的生命周期长于前者,直接注入NavController会引发内存泄漏。

理想实现方式

你编辑后的方案已经是正确方向,核心是让ViewModel不直接持有NavController,通过中间层NavigationUtil间接操作:

  1. NavigationUtil不再通过构造注入NavController,而是提供setController和clear方法绑定/解绑控制器。
  2. 在Fragment/Activity的生命周期方法中完成NavController的绑定与清理:
    class YourFragment : Fragment() {
        private val viewModel: DeviceHolderListViewModelImpl by viewModels()
        private val navigationUtil: NavigationUtil by inject()
    
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            val navController = findNavController()
            navigationUtil.setController(navController)
        }
    
        override fun onDestroyView() {
            navigationUtil.clear()
            super.onDestroyView()
        }
    }
    
  3. ViewModel中调用navigationUtil的方法即可发起导航,既实现了ViewModel驱动导航,又规避了生命周期不匹配的问题。

内容的提问来源于stack exchange,提问作者krtkush

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 12:07:48