如何使用共享的 ViewModel,并避免每次使用 Navigation Component 重复使用相同的实例

How to use a shared ViewModel, and avoid reusing the same instance of it every time with Navigation Component

我的应用程序由一个 Activity 和多个 Fragments 组成,在 "single Activity app model" 之后,因此我可以使用 [=89] 中的 Navigation Component 正确实现导航=] jetpack.

我的大部分屏幕 (Fragments) 都是独立的,互不依赖,因此它们使用自己的 ViewModel

某些功能需要涉及多个 Fragment 的导航。由于这些功能在它们之间共享通过 Fragments 来回传递的数据,我使用 共享 ViewModel(由 Google 推荐)。 我需要在所有关联的 Fragments 中使用共享 ViewModel 的相同实例,因为我需要 Fragments 共享共享 ViewModel.[=57= 的状态]

要在这些关联的 Fragments 中使用 ViewModel 的相同实例,我需要使用父 Activity(而不是 Fragment) 从 ViewModelProviders:

获取 ViewModel
val viewModel = ViewModelProviders.of(
            parentActivity, factory.create()
        ).get(SharedViewModel::class.java)

这行得通,但是会产生一个问题: 当连续导航到需要共享 ViewModel 的第一个 Fragment 时,ViewModelProviders.of() 将 return 与以前相同的 ViewModel 实例:ViewModelFragments 之间共享,但也在不同导航之间共享,以实现这样的功能。

我理解为什么会发生这种情况(Android 将 ViewModel 存储在 map 中,在使用 ViewModelProviders.of() 请求 ViewModel 时使用它),但是我不知道我应该如何正确实施 "shared ViewModel pattern"。

我看到的唯一解决方法是:

使用这两个选项,我将能够创建一个 ViewModel,它将在干预该功能的 Fragments 之间共享,并且每次导航到该功能时都会有所不同。

我在这里看到的问题是,这似乎违背了 Navigation Component 和单个 Activity 应用程序的基本原理。 以这种方式实现的每个功能都需要具有不同的导航图,因为它们将使用不同的导航主机。这会阻止我使用 Navigation Component.

的一些不错的功能

实现我想要的东西的正确方法是什么? 我是不是遗漏了什么,或者就是这样吗?

Navigation Component 之前,我会使用不同的 ActivitiesFragments 并使用与 Activity/Fragment 关联的 Dagger 范围来实现这个。但是我不确定只用一个 Activity`

来实现这个的最好方法是什么

您拥有共享 ViewModel 的相同实例,因为它属于 Activity - 很明显。我不确切知道你的用例,但通常当我需要做类似的事情时,我只是通过一些标识符从 Fragment's onCreateorCreateView 通知 ViewModel 。在你的情况下,它可能是这样的:

viewModel.onNavigatedTo("fragment1")

这样共享视图模型可以区分当前使用它的片段并相应地刷新状态。

我发现这可以从 2.1.0-alpha02

开始

来自: https://developer.android.com/jetpack/androidx/releases/navigation#2.1.0-alpha02

You can now create ViewModels that are scoped at a navigation graph level via the by navGraphViewModels() property delegate for Kotlin users or by using the getViewModelStore() API added to NavController. b/111614463

基本上:

  1. 在导航图编辑器中,创建一个嵌套图,并为其分配一个 id
  2. 提供 ViewModel 时,不要从 Activity 提供。相反,使用 Fragment.
  3. navGraphViewModels 扩展函数

示例:

导航图中的嵌套图

<navigation
        android:id="@+id/feature_nested_graph"
        android:label="Feature"
        app:startDestination="@id/firstFragment">
        <argument
            android:name="item_id"
            app:argType="integer" />
        <fragment
            android:id="@+id/firstFragment"
            [....]
        </fragment>
        [....]
    </navigation>

用于将 ViewModel 范围限定为 feature_nested_graph 嵌套导航间隙:

        val viewModel: SharedViewModel
                by fragment.navGraphViewModels(R.id.feature_nested_graph)

或者,如果正在注入 ViewModel 并且您正在为此使用自定义工厂:

        val viewModel: SharedViewModel
                by fragment.navGraphViewModels(R.id.feature_nested_graph) { factory2.create(assessmentId) }