我有两个
ViewModels
依赖于其他对象,而其他对象又可能依赖于 context
(SettingsDataStore
)。现在,为了将 context
排除在我的 ViewModel 之外,我遵循 inversion of control
原则。
我的代码看起来像这样:
// MainActivity.kt
private val Context.dataStore by preferencesDataStore(name = "settings")
val newsRepository = NewsRepository()
val newsDetailViewModel = NewsDetailViewModel()
lateinit var newsViewModel: NewsViewModel
lateinit var settingsViewModel: SettingsViewModel
@Composable
fun Navigation() {
val context = LocalContext.current
val settingsDataStore = SettingsDataStore(context.dataStore)
if (!::settingsViewModel.isInitialized) {
settingsViewModel = SettingsViewModel(settingsDataStore)
}
if (!::newsViewModel.isInitialized) {
newsViewModel = NewsViewModel(newsRepository, settingsDataStore)
}
...
如您所见,我正在可组合函数中创建
SettingsDataStore
,因为我需要访问 context
。然后我用它们的依赖构建ViewModels
。 但是,我怀疑这种注入是有问题的,原因有很多——如果你不小心,就会想到上下文无效和 ViewModels 的意外创建。
因此,我做了一些研究,发现这篇官方文档的文章推荐使用
ViewModelProvider.Factory
。不幸的是,我没有弄清楚如何使用工厂正确创建我的NewsViewModel
:
class NewsViewModel(
private val repo: NewsRepository,
private val settingsDataStore: SettingsDataStore
) : ViewModel() {
...
// Define ViewModel factory in a companion object
companion object {
val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(
modelClass: Class<T>,
extras: CreationExtras
): T {
// Get the Application object from extras
val application = checkNotNull(extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY])
// Create a SavedStateHandle for this ViewModel from extras
val savedStateHandle = extras.createSavedStateHandle()
return NewsViewModel(
// TODO HOW DO I ACCESS/CREATE THE DEPENDENCIES ??
) as T
}
}
}
}
所以我的问题是我如何用那个工厂创建我的
NewsViewModel
?我想坚持使用Android文档而不使用某种DI框架。
很抱歉这个问题很长,但我想详细说明,因为很多遵循正确的 MVVM 架构的人都会遇到这个问题。
是的,除了
ViewModel.Factory
的 create()
函数外,您绝不能在任何地方调用 ViewModel 构造函数。
您链接的文档中使用的模式是创建一个自定义应用程序类,在本例中名为
MyApplication
,并在其onCreate()
函数中创建依赖项并将它们存储在该类的属性中。像这样的东西:
class MyApplication: Application() {
lateinit var myRepository: MyRepository
private set
override fun onCreate() {
super.onCreate()
myRepository = MyRepository(this) // this, because an Application is a Context
}
}
在清单的
<application>
块中,添加 name=".MyApplication"
以告诉它使用哪个类,就像您对 <activity>
块所做的一样。
然后在工厂的
create()
函数中,您可以使用extras 检索应用程序实例。应用程序实例实际上是一个单例,因为操作系统会为您的进程实例化一次。该类由清单的应用程序名称属性定义,因此可以安全地将其转换为您在那里设置的类。
您可以使用您喜欢的
MyApplication
的任何属性传递给 ViewModel 的构造函数。
val myApplication = checkNotNull(extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY]) as MyApplication
return NewsViewModel(
myApplication.myRepository
) as T