Android:使用依赖项 ViewModelProviderFactory 初始化 ViewModel 的正确方法

问题描述 投票:0回答:1

我有两个

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 架构的人都会遇到这个问题。

android kotlin mvvm android-jetpack-compose android-viewmodel
1个回答
0
投票

是的,除了

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
© www.soinside.com 2019 - 2024. All rights reserved.