我有两个ViewModels,它们依赖于其他对象,而这些对象又可能依赖于context(SettingsDataStore).现在,为了将context排除在我的视图模型之外,我遵循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 = viewModel(initializer = { SettingsViewModel(settingsDataStore) })
    }

    if (!::newsViewModel.isInitialized) {
        newsViewModel =
            viewModel(initializer = { NewsViewModel(newsRepository, settingsDataStore) })
    }
...

如您所见,我在Composable函数中创建了SettingsDataStore,因为我需要访问context.然后,我将使用它们的依赖项构造ViewModels.However, I suspect this kind of injection is faulty because of plenty of reasons -- invalidation of the context and accidental creation of ViewModels if you are not careful comes to mind.

因此,我做了一些研究,发现这篇文章的official docs条推荐使用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
            }
        }
    }
}

所以我的问题是how do I create my 100 with that factory?我想坚持使用Android文档,而不是使用某种依赖注入框架.

很抱歉问了这么长的问题,但我想要详细说明,因为很多遵循适当MVVM架构的人都会遇到这个问题.

推荐答案

我最终通过使用我的Jetpack Compose应用程序中的ViewModelProvider helper类解决了这个问题.如果您仍在使用classic 的XML方法,请看一下@Tenfour04方法.还要记住,对于较大的项目,您应该考虑使用依赖注入框架. 一百零二

工厂本身需要进行更改,以便在其构造函数中将depeditions作为参数:

    class NewsViewModelFactory(
        private val newsRepository: NewsRepository,
        private val settingsDataStore: SettingsDataStore
    ) : ViewModelProvider.Factory {
        override fun <T : ViewModel> create(modelClass: Class<T>): T {
            if (modelClass.isAssignableFrom(NewsViewModel::class.java)) {
                @Suppress("UNCHECKED_CAST")
                return NewsViewModel(
                    settingsDataStore = settingsDataStore,
                    repo = newsRepository
                ) as T
            }
            throw IllegalArgumentException("Unknown ViewModel class")
        }
    }

现在,我们只需在Composable函数中调用ViewModelProvider并传递viewModelStoreOwner和具体工厂即可获得正确的ViewModel:

newsViewModel = ViewModelProvider(
        viewModelStoreOwner,
        NewsViewModel.NewsViewModelFactory(
            newsRepository, settingsDataStore
        )
    ).get()

这将确保正确创建ViewModel.每次后续调用都将返回已经创建的ViewModel,这正是我们想要的.

完整的示例现在如下所示:

// 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 viewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current) {
        "No ViewModelStoreOwner was provided via LocalViewModelStoreOwner"
    }

    val settingsDataStore = SettingsDataStore(context.dataStore)

    settingsViewModel = ViewModelProvider(
        viewModelStoreOwner,
        SettingsViewModel.SettingsViewModelFactory(settingsDataStore)
    ).get()


    newsViewModel = ViewModelProvider(
        viewModelStoreOwner,
        NewsViewModel.NewsViewModelFactory(
            newsRepository, settingsDataStore
        )
    ).get()
...

Android相关问答推荐

list 合并失败,AGP 8.3.0

NativeScript在`ns run android`上重复Kotlin类

如何在Android中打印到命令行

Jetpack Compose:带芯片的Textfield

当提供非状态对象时,compose 如何进行重组

使用 JNI 从 Android 应用程序中使用 Kotlin/Native 预构建共享库

从 HiltViewModel @Injection 访问 Application()

Jetpack Compose:如何绘制异形边框?

Android CompanionDeviceManager 永远找不到任何附近的蓝牙设备

Visual Studio 无法在 Android 上编译 .NET MAUI 项目

如何将可重用的 ExtendedFloatingActionButton 与可重用的脚手架链接起来

根据 Jetpack Compose 中的生命周期正确处理变量/函数

JCenter 是否永久关闭(10 月 31 日)?

如何在 Android 上移动 EditText 上的图标?

如何在 MAUI 项目中包含每个平台的现有 C++ 库?

如何从我的 android 应用程序中删除 QUERY_ALL_PACKAGES 权限?

如何在 kotlin 的 android room DB 中设置一对多关系

在jetpack compose中看不到圆角

Android:在模块 jetified-play-services-measurement 和 jetified-play-services-measurement-impl 中发现重复类

Xamarin 获取动态 ListView DataTemplate 中的按钮单击事件数据