使用 Hilt 进行依赖注入时,如何将运行时参数传递给 ViewModel 的构造函数?



我想知道如何在使用Hilt for DI时将运行时参数传递给ViewModel的构造函数?在使用Hilt之前,我有一个ViewModel,它看起来像这样:

class ItemViewModel(private val itemId: Long) : ViewModel() {
private val repo = ItemRepository(itemId) 
}
class ItemViewModelFactory(private val itemId: Long) : ViewModelProvider.Factory {
@Suppress("unchecked_cast")
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(ItemViewModel::class.java)) {
return ItemViewModel(itemId) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}

我在我的片段中创建了上面的ViewModel,如下所示:

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val args: ItemScreenFragmentArgs by navArgs()
val itemId = args.itemId
//Create the view model factory
val viewModelFactory = ItemViewModelFactory(application, itemId)
// Get a reference to the ViewModel associated with this fragment.
val itemViewModel = ViewModelProvider(this, viewModelFactory).get(ItemViewModel::class.java)
}

如果我的ItemViewModel构造函数没有itemId参数,那么使用Hilt的ViewModel和Fragment将如下所示:

class ItemViewModel
@ViewModelInject
constructor(private val repo: ItemRepository) : ViewModel() { }
@AndroidEntryPoint
class ItemFragment : Fragment() {
private val itemViewModel: ItemViewModel by viewModels ()
}

我想知道如何将从ItemFragment的NavArgs获得的itemId传递给ItemViewModel的构造函数?有办法和希尔特一起做这件事吗?

对于其他希望在使用Dagger-Hilt时将运行时参数传递给ViewModel的人,我是这样做的:

我遵循了这个使用AssistedInject库的示例中的代码。

我的代码现在如下:

class ItemViewModel
@AssistedInject
constructor(private val repo: ItemRepository, @Assisted private val itemId: Long) : ViewModel() {
init {
repo.itemId = itemId
}
@AssistedInject.Factory
interface AssistedFactory {
fun create(itemId: Long): ItemViewModel
}
companion object {
fun provideFactory(
assistedFactory: AssistedFactory,
itemId: Long
): ViewModelProvider.Factory = object : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return assistedFactory.create(itemId) as T
}
}
}
}
@InstallIn(FragmentComponent::class)
@AssistedModule
@Module
interface AssistedInjectModule {}
@AndroidEntryPoint
class ItemFragment : Fragment() {
private val args: ItemScreenFragmentArgs by navArgs()      
@Inject lateinit var itemViewModelAssistedFactory: ItemViewModel.AssistedFactory        
private val itemViewModel: ItemViewModel by viewModels {
ItemViewModel.provideFactory(itemViewModelAssistedFactory, args.itemId)
}    
}

没有AssistedInject有更好的解决方案,只需使用SavedStateHandle即可。

如果片段中的args包含userId:

val args: UserFragmentArgs by navArgs()
...
args.userId

然后,只需在savedStateHandle中,它就可以使用,而无需额外的工作。

class UserViewModel @Inject constructor (
private val state: SavedStateHandle
) : ViewModel() {
//Get the value of the userId here
val userId = state.get<String>("userId")

//Also you can do this using safe args
val args = UserFragmentArgs.fromSavedStateHandle(state)
val userId = args.userId
}

完整的实施细节:https://mattrobertson.dev/passing-safe-args-to-your-viewmodel-with-hilt-366762ff3f57

辅助注射现在得到Dagger的支持,而充气注射有了自己的回购。现在的语法如下:

import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
...


class ItemViewModel
@AssistedInject
constructor(private val repo: ItemRepository, @Assisted private val itemId: Long) : ViewModel() {
init {
repo.itemId = itemId
}
//-@AssistedInject.Factory
@AssistedFactory
interface AssistedFactory {
fun create(@Named("item_id") itemId: Long): ItemViewModel
}
companion object {
fun provideFactory(
assistedFactory: AssistedFactory,
itemId: Long
): ViewModelProvider.Factory = object : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return assistedFactory.create(itemId) as T
}
}
}
}
@InstallIn(FragmentComponent::class)
//-@AssistedModule
@Module
interface AssistedInjectModule {}
@AndroidEntryPoint
class ItemFragment : Fragment() {
private val args: ItemScreenFragmentArgs by navArgs()      
@Inject lateinit var itemViewModelAssistedFactory:ItemViewModel.AssistedFactory        
private val itemViewModel: ItemViewModel by viewModels {
ItemViewModel.provideFactory(itemViewModelAssistedFactory, args.itemId)
}    
}

基于Redek答案。更多关于这里

实际上,您可以使用工厂设计模式来创建需要通过的对象项目

这是工作,但我不确定这是不是正确的方式

class ItemRepository constructor(private val id: Int) {
}
class RepositoryFactory @Inject constructor() {
private var id: Int = 0
fun setId(id: Int) {
this.id = id
}
fun create(): ItemRepository = ItemRepository(id)
}

class ItemViewModel @ViewModelInject constructor(private val repositoryFactory: RepositoryFactory) : ViewModel() {
private var itemRepository: ItemRepository

init {
repositoryFactory.setId(45)
itemRepository = repositoryFactory.create()
}
}
@AndroidEntryPoint
class ItemFragment : Fragment() {
private val viewModel: ItemViewModel by viewModels ()
}

最新更新