如何使用一个 ViewModelFactory 为 Dagger 提供所有 ViewModel

How to use one ViewModelFactory to provide for all ViewModels with Dagger

如何实现通用 ViewModel 工厂来为我的所有项目 ViewModel 提供服务? 需要明确的是,我的 ViewModels 具有依赖性(作为构造函数参数)

嗯,有一个叫做 GithubBrowser,但它不是教程,而是一个项目。你应该知道 dagger for android 可以做到这一点。或者,您可以检查以下代码:

@Singleton
class DaggerViewModelFactory @Inject constructor(
    private val creators: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        val creator = creators[modelClass] ?: creators.entries.firstOrNull {
            modelClass.isAssignableFrom(it.key)
        }?.value ?: throw IllegalArgumentException("unknown model class $modelClass")
        try {
            @Suppress("UNCHECKED_CAST")
            return creator.get() as T
        } catch (e: Exception) {
            throw RuntimeException(e)
        }
    }
}

这部分将为您的整个应用程序创建一个 "generic" 视图模型。以这种方式, ViewModel 是用分配的参数创建的。之后,您需要在单例模块中实现工厂模块,并将其包含在组件中。

@Component(
    modules = [... ViewModelModule::class]
)
interface AppCompoenent{}

现在是有趣的部分:

    @Suppress("unused")
    @Module
    abstract class ViewModelModule {
        @Binds
        @IntoMap
        @ViewModelKey(MyViewModel::class)
        abstract fun bindsMyViewModel(viewModel: MyViewModel): ViewModel

    @Binds
    abstract fun bindsViewModelFactory(factory: DaggerViewModelFactory): ViewModelProvider.Factory
}

由于 dagger 支持多重绑定,您可以根据需要自由绑定 ViewModels

ViewModelKey:

@Target(
    AnnotationTarget.FUNCTION,
    AnnotationTarget.PROPERTY_GETTER,
    AnnotationTarget.PROPERTY_SETTER
)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)

您基本上是将值放入哈希图中。这些值是您的 ViewModel

点击构建! Done.After 你只是在你的片段中注入了一个 ViewModelProvider.Facory。在你的 ViewModel 中你可以做:

class MyViewModel @Inject constructor(
    private val dependency: YourDependency
) : ViewModel() {}

在评论中明确说明您的要求。首先,没有具体需要知道DaggerViewModelFactory内部发生了什么,虽然我不建议这样学习,因为我是"Always knowing what's happening"的铁杆粉丝。仅供参考,DaggerViewModelFactory 只是一个 class,它接受一个 Map,每个 class 扩展 ViewModel 作为键,并且 class 依赖项作为一个值。使用 Provider<T> 时,Dagger 知道如何找到这些依赖项,但在您调用 provider.get() 之前不会将它们带给您。将其视为延迟初始化。

现在检查modelClass.isAssignableFrom(it.key)。它只是检查 class 是否真的扩展了 ViewModel.

关于你的第二个问题,理解第一部分很重要。由于 Dagger 支持多重绑定,这意味着您可以使用 Map<Key, Value> 提供依赖项。例如,Map<HomeViewModel, Provider<ViewModel>> 基本上会告诉 dagger 给我 HomeViewModel 的依赖项。 Dagger 会说:如何知道哪些是 HomeViewModel 依赖项?然后您回答:我已经为此定义了一个 Key,它就是 HomeViewModel class 本身。所以你只需要创建一个注释,将它与 @Binds@IntoMap 结合起来,在后台 Dagger 将只执行 map.put(HomeViewModel::class, AndDependencies).