对于只有编译时依赖关系的ViewModels
,我使用来自架构组件的ViewModelProvider.Factory
,如下所示:
class ViewModelFactory<T : ViewModel> @Inject constructor(private val viewModel: Lazy<T>) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T = viewModel.get() as T
}
在我的
Activity
或Fragment
中,我通过以下方式得到ViewModel
:@Inject
lateinit var viewModelFactory: ViewModelFactory<ProductsViewModel>
在我的
ViewModel
需要一个仅在运行时可用的依赖项之前,这是正常的。场景是,我有一个
Product
列表,显示在RecyclerView
中。对于每个Product
,我都有ProductViewModel
。现在,
ProductViewModel
需要各种各样的依赖项,比如ResourceProvider
,AlertManager
等等,这些依赖项都是可用的编译时,我可以使用Inject
来constructor
它们,也可以使用Provide
来Module
它们。但是,除了上述依赖项之外,它还需要Product
对象,这只在运行时可用,因为我通过api调用获取产品列表。我不知道如何注入只在运行时可用的依赖项。所以我现在要做的是:
ProductsFragment.kt
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
productsAdapter = ProductsAdapter(context!!, products, R.layout.list_item_products, BR.productVm)
rvProducts.layoutManager = LinearLayoutManager(context)
rvProducts.addItemDecoration(RecyclerViewMargin(context, 10, 20))
rvProducts.adapter = productsAdapter
getProducts()
}
private fun getProducts() {
productsViewModel.getProducts()
.observe(this, Observer { productResponse: GetProductResponse ->
products.clear()
productsAdapter?.notifyDataSetChanged()
val productsViewModels = productResponse.data.map { product ->
// Here product is fetched run-time and alertManager etc are
// injected into Fragment as they are available compile-time. I
// don't think this is correct approach and I want to get the
// ProductViewModel using Dagger only.
ProductViewModel(product, resourceProvider,
appUtils, alertManager)
}
products.addAll(productsViewModels)
productsAdapter?.notifyDataSetChanged()
})
}
ProductsAdapter
将ProductViewModel
与list_item_products
布局绑定。正如我在代码的注释中提到的,我不想创建我自己,相反,我只想从Dagger获得它。我也相信正确的方法是将
ProductViewModel
直接注入ProductsAdapter
,但同时,我需要告诉dagger从哪里可以获得Fragment
的对象,该对象在运行时可用,它最终会为我提出相同的问题。任何实现这一目标的指南或指导都是非常棒的。
最佳答案
您正朝着正确的方向希望注入依赖项,而不是像使用ProductViewModel
那样创建依赖项。但是,是的,您不能注入productviewmodel,因为它需要一个只在运行时可用的产品。
解决此问题的方法是创建ProductViewModel的工厂:
class ProductViewModel(
val product: Product,
val resourceProvider: ResourceProvider,
val appUtils: AppUtils,
val alertManager: AlertManager
) {
// ...
}
class ProductViewModelFactory @Inject constructor(
val resourceProvider: ResourceProvider,
val appUtils: AppUtils,
val alertManager: AlertManager
) {
fun create(product: Product): ProductViewModel {
return ProductViewModel(product, resourceProvider, appUtils, alertManager)
}
}
然后在
ProductViewModelFactory
类中注入ProductsFragment
,并在产品可用时调用productViewModelFactory.create(product)
。当您的项目开始变得更大,并且您看到这种模式重复出现时,请考虑使用AssistedInject来减少样板文件。