【问题标题】:How to pass runtime parameters to a ViewModel's constructor when using Hilt for dependency injection?使用 Hilt 进行依赖注入时,如何将运行时参数传递给 ViewModel 的构造函数?
【发布时间】:2020-12-13 20:28:36
【问题描述】:

我想知道如何在将 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")
}

我在我的片段中创建了上面的 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 的构造函数?有没有办法用 Hilt 做到这一点?

【问题讨论】:

    标签: android android-fragments dependency-injection android-viewmodel dagger-hilt


    【解决方案1】:

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

    我遵循了来自this example 的代码,它使用了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)
        }    
    }
    

    【讨论】:

    • thx 但我在刀柄 2.35 中遇到如下错误:ViewModel 构造函数应使用 [at]Inject 而不是 [at]AssistedInject 进行注释。 [Hilt] 处理未完成。有关详细信息,请参阅上面的错误。
    • @gturedi 您必须在视图模型中删除 HiltViewModel 才能使用 AssistedInject 但在最新更新中,您可以使用 SavedStateHandle 传递参数。看这里,github.com/google/dagger/issues/2287#issuecomment-771671159
    • 是的,后来我注意到了,我工作了。谢谢
    • 我遵循了这种方法。我一直收到AssistedInjectModule is missing an @InstallIn annotation,即使我保留了@InstallIn(SingletonComponent::class)
    【解决方案2】:

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

    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 的回答。更多关于here

    【讨论】:

      【解决方案3】:

      其实你可以使用工厂设计模式来创建需要item传递的对象

      这是可行的,但我不确定它是否正确

      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 ()
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-02-07
        相关资源
        最近更新 更多