android - Dagger2.10+ : Inject ViewModel in Fragment/Activity which has run-time dependencies

标签 android dependency-injection dagger-2 android-viewmodel assisted-inject

对于仅具有编译时依赖性的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
}

在我的 ActivityFragment 中,我通过以下方式获取 ViewModel:

@Inject
lateinit var viewModelFactory: ViewModelFactory<ProductsViewModel>

在我的 ViewModel 需要一个仅在运行时 可用的依赖项之前,它工作正常。

场景是,我有一个 Product 列表,我正在 RecyclerView 中显示它。对于每个 Product,我都有 ProductViewModel

现在,ProductViewModel 需要各种依赖项,如 ResourceProviderAlertManager 等,它们在编译时可用,我可以 使用 constructor 注入(inject)它们,或者我可以使用 Module Provide 它们。但是,除上述依赖项外,它还需要 Product 对象,该对象仅在运行时可用,因为我通过 API 调用获取产品列表。

我不知道如何注入(inject)仅在运行时可用的依赖项。所以我现在正在做以下事情:

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

ProductsAdapterProductViewModellist_item_products 布局绑定(bind)。

正如我在代码的注释中提到的,我不想自己创建 ProductViewModel,而是只想从 dagger 中创建它。我也相信正确的方法是将 ProductsAdapter 直接注入(inject)Fragment 中,但是我还需要告诉 dagger 它从哪里来可以获取 ProductViewModelProduct 对象,该对象在运行时可用,它最终对我来说是同一个问题。

任何实现这一目标的指南或指示都非常棒。

最佳答案

您想要注入(inject)依赖项而不是像使用 ProductViewModel 那样创建依赖项的方向是正确的。但是,是的,您不能注入(inject) 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 类中注入(inject) ProductViewModelFactory,并在 Product 可用时调用 productViewModelFactory.create(product)


随着您的项目开始变大并且您看到这种模式不断重复,请考虑使用 AssistedInject减少样板文件。

关于android - Dagger2.10+ : Inject ViewModel in Fragment/Activity which has run-time dependencies,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57752301/

相关文章:

android - 需要登录的 Android 应用程序的 OpenID

android - MPAndroidChart:如何自定义条形值标签

c# - StructureMap 中的命名实例和默认实例?

dependency-injection - 在 Orchard 内消费非 Orchard 服务类别

java - AppCompatActivity 未实现 LifecycleOwner

java - 当我从 android 刷新时出现 Android TCP 错误 :onClick

c# - 连续记录方法、调用和退出时间

java - Android 上的 Dagger 2。存储和访问 @Singleton 组件的不同方式

java - 带有 MVP 的 Dagger 2,避免在 View 重新创建时创建额外的演示者对象

android gradle构建: Generated class list does not exist