ViewModel注入NavController失败及ViewModel发起导航方案咨询
原实现代码
ViewModel代码
@HiltViewModel class DeviceHolderListViewModelImpl @Inject constructor( private val fetchUsersUseCase: FetchUsersUseCase, private val navigationUtil: NavigationUtil ) : DeviceHolderListViewModel, ViewModel() { // 尝试在此处调用navigationUtil }
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() } }
NavigationModule代码
@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() } }
问题解析与解决方案
错误原因
- 作用域不匹配:
@HiltViewModel标注的ViewModel属于ViewModelComponent作用域,而你的NavigationModule安装在ActivityComponent中,Dagger无法跨作用域提供依赖,导致NavController无法注入到ViewModel依赖的NavigationUtil中。 - 生命周期风险:NavController绑定的是Activity/Fragment的生命周期,ViewModel的生命周期长于前者,直接注入NavController会引发内存泄漏。
理想实现方式
你编辑后的方案已经是正确方向,核心是让ViewModel不直接持有NavController,通过中间层NavigationUtil间接操作:
NavigationUtil不再通过构造注入NavController,而是提供setController和clear方法绑定/解绑控制器。- 在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() } } - ViewModel中调用
navigationUtil的方法即可发起导航,既实现了ViewModel驱动导航,又规避了生命周期不匹配的问题。
内容的提问来源于stack exchange,提问作者krtkush
相关产品推荐
相关产品推荐

