如何使用共享的 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
实例:ViewModel
在 Fragments
之间共享,但也在不同导航之间共享,以实现这样的功能。
我理解为什么会发生这种情况(Android 将 ViewModel 存储在 map
中,在使用 ViewModelProviders.of()
请求 ViewModel
时使用它),但是我不知道我应该如何正确实施 "shared ViewModel pattern"。
我看到的唯一解决方法是:
- 为使用
Fragment
和共享 ViewModel
的功能创建一个不同的 Activity
- 使用嵌套
Fragments
,并使用公共父 Fragment
作为使用 Fragment
和共享 ViewModel
的特征
使用这两个选项,我将能够创建一个 ViewModel
,它将在干预该功能的 Fragments
之间共享,并且每次导航到该功能时都会有所不同。
我在这里看到的问题是,这似乎违背了 Navigation Component
和单个 Activity
应用程序的基本原理。
以这种方式实现的每个功能都需要具有不同的导航图,因为它们将使用不同的导航主机。这会阻止我使用 Navigation Component
.
的一些不错的功能
实现我想要的东西的正确方法是什么?
我是不是遗漏了什么,或者就是这样吗?
在 Navigation Component
之前,我会使用不同的 Activities
和 Fragments
并使用与 Activity
/Fragment
关联的 Dagger
范围来实现这个。但是我不确定只用一个 Activity`
来实现这个的最好方法是什么
您拥有共享 ViewModel
的相同实例,因为它属于 Activity
- 很明显。我不确切知道你的用例,但通常当我需要做类似的事情时,我只是通过一些标识符从 Fragment's
onCreate
或 orCreateView
通知 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
基本上:
- 在导航图编辑器中,创建一个嵌套图,并为其分配一个 id
- 提供
ViewModel
时,不要从 Activity 提供。相反,使用
Fragment
. 的 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) }
我的应用程序由一个 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
实例:ViewModel
在 Fragments
之间共享,但也在不同导航之间共享,以实现这样的功能。
我理解为什么会发生这种情况(Android 将 ViewModel 存储在 map
中,在使用 ViewModelProviders.of()
请求 ViewModel
时使用它),但是我不知道我应该如何正确实施 "shared ViewModel pattern"。
我看到的唯一解决方法是:
- 为使用
Fragment
和共享ViewModel
的功能创建一个不同的 - 使用嵌套
Fragments
,并使用公共父Fragment
作为使用Fragment
和共享ViewModel
的特征
Activity
使用这两个选项,我将能够创建一个 ViewModel
,它将在干预该功能的 Fragments
之间共享,并且每次导航到该功能时都会有所不同。
我在这里看到的问题是,这似乎违背了 Navigation Component
和单个 Activity
应用程序的基本原理。
以这种方式实现的每个功能都需要具有不同的导航图,因为它们将使用不同的导航主机。这会阻止我使用 Navigation Component
.
实现我想要的东西的正确方法是什么? 我是不是遗漏了什么,或者就是这样吗?
在 Navigation Component
之前,我会使用不同的 Activities
和 Fragments
并使用与 Activity
/Fragment
关联的 Dagger
范围来实现这个。但是我不确定只用一个 Activity`
您拥有共享 ViewModel
的相同实例,因为它属于 Activity
- 很明显。我不确切知道你的用例,但通常当我需要做类似的事情时,我只是通过一些标识符从 Fragment's
onCreate
或 orCreateView
通知 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
基本上:
- 在导航图编辑器中,创建一个嵌套图,并为其分配一个 id
- 提供
ViewModel
时,不要从 Activity 提供。相反,使用Fragment
. 的
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) }