注入 ViewModels 的最佳方式?
best way for injecting ViewModels?
我过去使用过 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
本身也是混乱的!我认为您不应该在大型项目中专门这样做。那么有哪些替代方法可以做到这一点?
dagger 如何解决这个问题?
实际上有很好的替代品。
第一个:人们往往会忘记的第一个是每个 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
}
}
并在 activity 中:
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
}
}
并在 activity 或片段中像这样使用它:
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中实例化你的匕首组件,有很多方法可以做到这一点,我只是使用工厂接口:
class MyApplication: Application() {
val component: AppComponent by lazy {
DaggerAppComponent.factory().create(applicationContext)
}
}
别忘了在清单中添加 MyApplication
。
然后在你的 activity 中,你可以注入 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 }
第四:你总是可以使用匕首的多重绑定。这样,您将 ViewModelFactory 注入 activity 或片段,然后从中检索 viewModel。有了这个,你告诉匕首把你所有的 viewModels 放在一个地图中,并将它注入 ViewModelFactory。你通过注释帮助 dagger 找到 viewModels。并且您还使用另一个注释告诉 dagger 他们的密钥是什么。您在另一个模块中完成所有这些工作,对于每个视图模型,您都需要在视图模型模块中使用一个函数。然后在你的大型工厂中而不是 switch/case ,你告诉匕首根据其类型从地图中获取所需的视图模型。这是一个服务定位器(反?)模式。这本身就是另一个话题,这个答案已经太长了。参考this or this
总结 : 我想如果你用的是匕首,第三个肯定是赢家。多重绑定也很好,但是每次添加视图模型时,您都需要记住在视图模型模块中也添加一个函数(就像 Koin 一样)。如果你不使用匕首,我认为第二种方式是最好的,但你应该决定什么是最适合你的。
公印也很棒!
我过去使用过 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
本身也是混乱的!我认为您不应该在大型项目中专门这样做。那么有哪些替代方法可以做到这一点?
dagger 如何解决这个问题?
实际上有很好的替代品。
第一个:人们往往会忘记的第一个是每个 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
}
}
并在 activity 中:
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
}
}
并在 activity 或片段中像这样使用它:
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中实例化你的匕首组件,有很多方法可以做到这一点,我只是使用工厂接口:
class MyApplication: Application() {
val component: AppComponent by lazy {
DaggerAppComponent.factory().create(applicationContext)
}
}
别忘了在清单中添加 MyApplication
。
然后在你的 activity 中,你可以注入 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 }
第四:你总是可以使用匕首的多重绑定。这样,您将 ViewModelFactory 注入 activity 或片段,然后从中检索 viewModel。有了这个,你告诉匕首把你所有的 viewModels 放在一个地图中,并将它注入 ViewModelFactory。你通过注释帮助 dagger 找到 viewModels。并且您还使用另一个注释告诉 dagger 他们的密钥是什么。您在另一个模块中完成所有这些工作,对于每个视图模型,您都需要在视图模型模块中使用一个函数。然后在你的大型工厂中而不是 switch/case ,你告诉匕首根据其类型从地图中获取所需的视图模型。这是一个服务定位器(反?)模式。这本身就是另一个话题,这个答案已经太长了。参考this or this
总结 : 我想如果你用的是匕首,第三个肯定是赢家。多重绑定也很好,但是每次添加视图模型时,您都需要记住在视图模型模块中也添加一个函数(就像 Koin 一样)。如果你不使用匕首,我认为第二种方式是最好的,但你应该决定什么是最适合你的。
公印也很棒!