Dagger2.10+:在具有运行时依赖项的Fragment/Activity中注入ViewModel

5

对于仅具有编译时依赖关系的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注入它们,也可以使用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()
            })
}
ProductsAdapterProductViewModellist_item_products 布局绑定。正如我在代码注释中提到的,我不想自己创建 ProductViewModel ,而是希望仅从dagger获取它。我认为正确的方法是直接将 ProductsAdapter 注入到 Fragment 中,但是这样做之后,我还需要告诉 dagger 在运行时从哪里获取 ProductViewModelProduct 对象,这对我来说仍然是一个问题。如果有任何指导或方向来实现这一点,那将非常棒。

4
你尝试过AssistedInject吗?我认为这正是你要找的。如果你想知道如何使用它,我还有这篇文章 - coroutineDispatcher
1
请参考以下两个链接,了解Dagger辅助注入的示例: 1 2 - denvercoder9
1
@coroutineDispatcher 感谢链接,我会快速检查 :) - Sandip Fichadiya
1个回答

6
你想要实现依赖注入而不是像使用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)
    }  
}

然后在您的ProductsFragment类中注入ProductViewModelFactory,并在产品可用时调用productViewModelFactory.create(product)


随着项目越来越大,并且看到这个模式重复出现时,请考虑使用AssistedInject来减少样板代码。


1
看起来是一个可行的方案。谢谢,我很快就会通过实现来检查它 :) - Sandip Fichadiya

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接