5

对于ViewModels只有编译时依赖项,我使用ViewModelProvider.Factoryfrom Architecture 组件,如下所示:

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需要各种依赖项,例如 等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()
            })
}

ProductsAdapterProductViewModel将与list_item_products布局绑定。

正如我在代码中的注释中提到的,我不想创建ProductViewModel自己,而是只希望从匕首中获得它。我也相信正确的方法是将直接注入.ProductsAdapterFragmentProductProductViewModel

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

4

1 回答 1

6

您想要注入依赖项而不是像使用ProductViewModel. 但是,是的,您不能注入 ProductViewModel 因为它需要一个仅在运行时可用的产品。

这个问题的解决方法是创建一个ProductViewModel的Factory:

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来减少样板文件。

于 2019-09-04T09:52:28.290 回答