Dagger2.10+:在具有 运行 时间依赖性的 Fragment/Activity 中注入 ViewModel
Dagger2.10+: Inject ViewModel in Fragment/Activity which has run-time dependencies
对于只有 编译时 依赖项的 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
需要一个仅在 运行-time.
时可用的依赖项之前,它工作正常
情况是,我有一个 Product
的列表,我正在 RecyclerView
中显示。对于每个 Product
,我有 ProductViewModel
.
现在,ProductViewModel
需要编译时可用的各种依赖项,例如 ResourceProvider
、AlertManager
等,我可以 Inject
使用 [=29] =] 或者我可以 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
布局绑定。
正如我在代码的评论中提到的,我不想创建 ProductViewModel
我自己,而是我只想从匕首中创建它。我也相信正确的方法是 Inject ProductsAdapter
直接进入 Fragment
,但是我还需要告诉 dagger 从哪里可以得到 Product
ProductViewModel
的对象,它在 运行 时间可用,它最终对我来说是同一个问题。
任何实现此目标的指南或方向都非常棒。
您想要注入依赖项而不是像使用 ProductViewModel
那样创建依赖项的方向是正确的。但是,是的,您不能注入 ProductViewModel,因为它需要一个仅在运行时可用的 Product。
这个问题的解决方案是创建 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)
}
}
然后在您的 ProductsFragment
class 中注入 ProductViewModelFactory
,并在产品可用时调用 productViewModelFactory.create(product)
。
随着您的项目开始变大并且您看到这种模式不断重复,请考虑使用 AssistedInject 来减少样板文件。
对于只有 编译时 依赖项的 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
需要一个仅在 运行-time.
情况是,我有一个 Product
的列表,我正在 RecyclerView
中显示。对于每个 Product
,我有 ProductViewModel
.
现在,ProductViewModel
需要编译时可用的各种依赖项,例如 ResourceProvider
、AlertManager
等,我可以 Inject
使用 [=29] =] 或者我可以 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
布局绑定。
正如我在代码的评论中提到的,我不想创建 ProductViewModel
我自己,而是我只想从匕首中创建它。我也相信正确的方法是 Inject ProductsAdapter
直接进入 Fragment
,但是我还需要告诉 dagger 从哪里可以得到 Product
ProductViewModel
的对象,它在 运行 时间可用,它最终对我来说是同一个问题。
任何实现此目标的指南或方向都非常棒。
您想要注入依赖项而不是像使用 ProductViewModel
那样创建依赖项的方向是正确的。但是,是的,您不能注入 ProductViewModel,因为它需要一个仅在运行时可用的 Product。
这个问题的解决方案是创建 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)
}
}
然后在您的 ProductsFragment
class 中注入 ProductViewModelFactory
,并在产品可用时调用 productViewModelFactory.create(product)
。
随着您的项目开始变大并且您看到这种模式不断重复,请考虑使用 AssistedInject 来减少样板文件。