我试图注入navController
到我的ViewModel -
ViewModel——
@HiltViewModel
class DeviceHolderListViewModelImpl @Inject constructor(
private val fetchUsersUseCase: FetchUsersUseCase,
private val navigationUtil: NavigationUtil
) : DeviceHolderListViewModel, ViewModel() {
// Trying to access navigationUtil here
}
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不能在没有@Inject构造函数或@Provides-annotated方法。
是否因为我试图从ViewModel
访问navController
,而它应该从片段或活动访问?
我的目标是从ViewModel开始导航。理想情况下我该怎么做呢?
编辑:根据@DAA的回答更改-
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()
}
}
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()
}
}
你不能以这种方式直接注入NavController
与Hilt,因为你只能从你的活动/片段调用Navigation.findNavController
,但是你正在采取的NavigationUtil
方法是一个很好的方法,以便从ViewModel访问导航。
你只需要做一些改变:
- 代替将
NavController
作为NavigationUtil
的参数传递,创建两个方法setNavController
和clearNavController
。 - 使
NavigationUtil
作用域为ViewModel,用@ViewModelScoped
注释提供方法。 - 将
NavigationUtil
注入到Activity中,并在onCreate
方法中调用setNavController
,在onDestroy
方法中调用clearNavController
。
现在你的NavigationUtil
可以访问NavController
,当活动被重新创建时它被更新(当它被销毁时清除),你可以从你的ViewModel访问它。
你可以检查我给了一个非常类似的问题的答案在这里:如何注入' memorbernavcontroller '从jetpack撰写到一个活动使用柄?
我会建议一个不同的方法。
我通常做一个Navigatior
类,这是在Activity
和你的ViewModel
注入。ViewModel
调用导航器方法,活动订阅它们。
免责声明:以下代码是根据我的记忆编写的,可能在语法上不正确。请随意改进。
sealed interface NavEvent {
data class Navigate(val directions: NavDirections): NavEvent
object Pop(): NavEvent
data class PopForResult(val requestKey: String, val result: Bundle): NavEvent
}
@Singleton
class Navigator @Inject constructor() {
private val _navigateFlow = MutableSharedFlow<NavEvent>
val navigateFlow: SharedFlow = _navigateFlow
suspend fun navigate(nav: NavDirections) {
_navigateFlow.emit(Navigate(directions))
}
suspend fun pop() {
_navigateFlow.emit(Pop)
}
}
@HiltViewModel
class MyVm @Inject constructor(
private val navigator: Navigator
) : ViewModel() {
fun onClickSmth() {
viewModelScope.launch {
navigator.navigate(MyFragmentDirections.actionToSomewhere())
}
}
}
@AndroidEntryPoint
class MainActivity : Activity {
@Inject lateinit var navigator: Navigator
private lateinit var navController: NavController = TODO()
fun onCreate() {
lifecycleScope.launch {
navigator.navigateFlow.collect(this::onNavEvent)
}
}
private fun onNavEvent(event: NavEvent) {
when (navEvent) {
Navigate -> navController.navigate(navEvent.directions)
Pop -> navController.popBackStack()
PopForResult -> {
navController.previousBackStackEntry
?.savedStateHandle
?.set(event.requestKey, event.result)
navController.popBackStack()
}
}
}
}