实际上,有很好的替代方案。
首先:人们往往会忘记的第一个方法是为每个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,请先学习它,然后再阅读本部分。
你需要告诉Dagger你想要获取一个FirstViewModel
的实例。这个位置在AppComponent
中。
@Component(modules = [AppModule::class])
interface AppComponent {
val applicationContext: Context
val firstViewModel: FirstViewModel
...
@Component.Factory
interface Factory {
fun create(@BindsInstance applicationContext: Context): AppComponent
}
}
在你的appModule中,你需要使用@Provides
注解告诉dagger如何提供FirstViewModel
的依赖项。然后你需要在应用程序类中实例化你的dagger组件,有很多方法可以做到这一点,我只是使用了工厂接口:
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将所有viewModels放入一个映射中,并将其注入到ViewModelFactory中。通过对它们进行注释,您帮助dagger找到viewModels。还使用另一个注释告诉dagger它们的键是什么。在另一个模块中执行所有操作,并且对于您需要的每个viewModel,您的viewModel模块中都需要一个函数。然后,在庞大的工厂中,您可以告诉dagger根据其类型从映射中获取所需的viewModel,而不是使用switch/case。这是一种服务定位器(反?)模式。这是另一个话题,本答案已经太长了。请参考这篇文章或这篇文章。
总结:我认为如果您正在使用dagger,则第三种方式肯定是赢家。multibinding也很好,但每次添加一个viewmodel时,您都需要记住在viewmodelModule中添加一个函数(就像Koin一样)。如果您没有使用dagger,则在我看来,第二种方法是最好的,但您应该决定哪种方式最适合您。Koin也很棒!
ViewModelStateOwner
时,有什么推荐吗? - undefined