【问题标题】:best way for injecting ViewModels?注入 ViewModel 的最佳方法?
【发布时间】:2021-01-04 17:21:44
【问题描述】:

我过去使用过 koin,用 koin 注入 viewModel 是一个单一的衬里。我需要知道没有它怎么做! 我们应该在 ViewModelFactory 中为不同的视图模型使用一个大的switch/case 吗?

class ViewModelFactory: ViewModelProvider.Factory {

    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
       return when(modelClass) {
            is FirstViewModel -> FirstViewModel()
            is SecondViewModel -> SecondViewModel()
                ...
        }
    }
}

但是我必须将所有视图模型的所有依赖项注入工厂。那真是乱七八糟的代码。即使没有switch/case 本身也很混乱!我认为您不应该在大型项目中专门这样做。那么这样做的替代方法是什么?
匕首如何帮助解决这个问题?

【问题讨论】:

标签: android mvvm dependency-injection viewmodel dagger-2


【解决方案1】:

其实还有很多不错的选择。

first:人们往往会忘记的第一个是每个 ViewModel 都有一个 ViewModelfactory,这比拥有一个大型工厂要好得多。这就是我们所说的单一职责原则

class FirstViewModelFactory(val firstDependency: SomeClass, val secondDependency: SomeOtherClass): ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        FirsViewModel(firstDependency, secondDependency) as T
    }
}

在活动中:

val viewModel: FirstViewModel = ViewModelProvider(this, FirstViewModelFactory(first, second))[FirstViewModel::class.java]

第二:如果您希望所有 ViewModel 都只有一个工厂,您可以定义一个带有 lambda 的通用工厂:

class ViewModelFactory<VM: ViewModel>(val provider: () -> VM): ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return  provider() as T
    }
}

并在活动或片段中像这样使用它:

val ViewModel: FirstViewModel = ViewModelProvider(this, ViewModelFactory<FirstViewModel>{
    FirstViewModel(first, second)
})[FirstViewModel::class.java]

这实际上让您的生活更轻松。但还是可以改进的

第三:你可以告诉dagger如何为你提供FirstViewModel及其依赖。 如果您不知道如何使用匕首,请尝试学习它然后阅读此部分。 你需要告诉 dagger 你想要一个FirstViewModle 的实例。那个地方在AppComponent

@Component(modules = [AppModule::class])
interface AppComponent {
    val applicationContext: Context
    val firstViewModel: FirstViewModel
    
    ...

    @Component.Factory
    interface Factory {
        fun create(@BindsInstance applicationContext: Context): AppComponent
    }
}

在你的 appModule 中,你需要告诉 dagger 如何使用 @Provides 注解来提供 FirstViewModel 的依赖关系。那么你需要在应用程序类中实例化你的匕首组件,有很多方法可以做到这一点,我只是使用工厂接口:

class MyApplication: Application() {
    val component: AppComponent by lazy {
        DaggerAppComponent.factory().create(applicationContext)
    }
}

不要忘记在清单中添加MyApplication

然后在您的活动中,您可以注入 viewModel 而无需担心依赖关系:

val appComponent = (application as MyApplication).appComponent

val viewModel: FirstViewModel = ViewModelProvider(this, ViewModelFactory<FirstViewModel>{
    appComponent.firstViewModel
})[FirstViewModel::class.java]

您可以使用lazy 和扩展函数使其更加美观和可读,您最终可以拥有这样的东西:

private val viewModel: FirstViewModel by injectVmWith { appInjector.firstViewModel }

第四:你可以随时使用dagger的multibinding。有了这个,您将 ViewModelFactory 注入到活动或片段中,然后从中检索 viewModel。有了这个,你告诉 dagger 把你所有的 viewModel 放在一个地图中,然后把它注入到 ViewModelFactory。你通过注释来帮助 dagger 找到 viewModel。并且您还使用另一个注释告诉 dagger 他们的密钥是什么。您在另一个模块中完成所有这些操作,并且对于每个视图模型,您都需要在视图模型模块中添加一个函数。然后,您无需在大型工厂中使用 switch/case,而是告诉 dagger 根据其类型从地图中获取所需的视图模型。这是一个服务定位器(反?)模式。这本身就是另一个话题,这个答案已经太长了。参考thisthis

总结:我认为如果你使用匕首,第三把绝对是赢家。多重绑定也很好,但是每次添加视图模型时,您都需要记住在 viewmodelModule 中添加一个函数(就像 Koin 一样)。如果您不使用匕首,我认为第二种方式是最好的,但您应该决定哪种方式最适合您。
Koin也很棒!

【讨论】:

猜你喜欢
  • 2012-11-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-09
  • 2013-09-14
  • 2016-06-27
  • 2017-10-10
  • 2018-10-20
相关资源
最近更新 更多