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

Red*_*dek 12 android dependency-injection android-fragments android-viewmodel dagger-hilt

我想知道如何在将 Hilt 用于 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")
}
Run Code Online (Sandbox Code Playgroud)

我在我的片段中创建了上面的 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)
}
Run Code Online (Sandbox Code Playgroud)

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

class ItemViewModel
@ViewModelInject
constructor(private val repo: ItemRepository) : ViewModel() { }

@AndroidEntryPoint
class ItemFragment : Fragment() {
    private val itemViewModel: ItemViewModel by viewModels ()
}
Run Code Online (Sandbox Code Playgroud)

我想弄清楚如何将我从 ItemFragment 的 NavArgs 获得的 itemId 传递给 ItemViewModel 的构造函数?有没有办法用 Hilt 做到这一点?

Sal*_*nna 12

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

如果片段中的 args 包含 userId:

val args: UserFragmentArgs by navArgs()
...
args.userId
Run Code Online (Sandbox Code Playgroud)

然后只需在您的 savingStateHandle 中即可使用它,无需额外的工作。

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
}
Run Code Online (Sandbox Code Playgroud)

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


Red*_*dek 10

对于其他希望在使用 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)
    }    
}
Run Code Online (Sandbox Code Playgroud)

  • 谢谢,但我在 hilt 2.35 中遇到了如下错误:ViewModel 构造函数应该用 [at]Inject 而不是 [at]AssistedInject 注释。[Hilt] 处理未完成。有关详细信息,请参阅上面的错误。 (2认同)
  • @gturedi 您必须在视图模型中删除 HiltViewModel 才能使用 AssistedInject,但在最新更新中,您可以使用 SavedStateHandle 传递参数。在这里看到它,https://github.com/google/dagger/issues/2287#issuecomment-771671159 (2认同)

Leu*_*uss 5

Dagger 现在支持辅助注入,并且 InflationInjection 拥有自己的存储库。现在的语法如下:

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)
    }    
}
Run Code Online (Sandbox Code Playgroud)

基于雷德克的回答。更多相关内容请点击此处