希尔特在同一活动中创建视图模型的不同实例



在最近从Dagger迁移到Hilt之后,我开始观察到ViewModels非常奇怪的行为。下面是代码片段:


@HiltAndroidApp
class AndroidApplication : Application() {}
@Singleton
class HomeViewModel @ViewModelInject constructor() :
ViewModel() {}
@AndroidEntryPoint
class HomeFragment : Fragment(R.layout.fragment_home) {
private val homeViewModel by viewModels<HomeViewModel>()
override fun onResume() {
super.onResume()
Timber.i("hashCode: ${homeViewModel.hashCode()}")
}
}

@AndroidEntryPoint
class SomeOtherFragment : Fragment(R.layout.fragment_home) {
private val homeViewModel by viewModels<HomeViewModel>()
override fun onResume() {
super.onResume()
Timber.i("hashCode: ${homeViewModel.hashCode()}")
}
}

哈希代码的值在所有片段中都不一致。我无法弄清楚我还缺少什么才能在活动中生成视图模型的单例实例。

我正在使用单个活动设计,并添加了所有必需的依赖项。

当您使用by viewModels时,您将创建一个作用域为该单个片段的 ViewModel - 这意味着每个片段都将拥有该 ViewModel 类的单独实例。如果您希望将单个 ViewModel 实例的范围限定为整个活动,则需要使用by activityViewModels

private val homeViewModel by activityViewModels<HomeViewModel>()

Ian 说的是正确的,by viewModels是片段的扩展函数,它将使用 Fragment 作为 ViewModelStoreOwner。

如果需要将其范围限定为活动,可以使用by activityViewModels

但是,您通常不需要活动范围的视图模型。它们在单个活动应用程序中实际上是全局的。

要创建活动全局无状态组件,可以使用 Hilt 中的@ActivityRetainedScope。这些将可用于在活动或片段中创建的视图模型。

要创建有状态的保留组件,您应该依靠 ~~@ViewModelInject@Assisted~~@HiltViewModel@Inject constructor来获取 SavedStateHandle。

在这一点上,您很可能真的想要一个 NavGraph 范围的视图模型,而不是活动范围的视图模型。

要将 SavedStateHandle 获取到片段内的 NavGraph 范围的视图模型中,请使用val vm = androidx.hilt.navigation.fragment.hiltNavGraphViewModels(R.navigation.nav_graph_id)

如果您没有使用Hilt,则可以使用= navGraphViewModels但您可以使用默认的ViewModelProvider.Factory或CreationExtras获取SavedStateHandle。

这是ianhanniballake提到的另一种解决方案。它允许您在片段之间共享视图模型,同时不将其分配给活动,因此您可以避免像EpicPandaForce所说的那样在单个活动中创建全局视图模型。如果您使用的是导航组件,则可以创建要共享视图模型的片段的嵌套导航图(请遵循本指南:嵌套导航图(

在每个片段中:

private val homeViewModel: HomeViewModel
by navGraphViewModels(R.id.nested_graph_id){defaultViewModelProviderFactory}

当您导航出嵌套图形时,视图模型将被删除。当您导航回嵌套图形时,将重新创建它。

正如此处的其他帖子所提到的,使用by activityViewModels<yourClass>()会将虚拟机的范围限定为整个活动的生命周期,使其成为一个全局范围,包括整个应用程序,如果它是每个人都使用且 Google 推荐的一种活动架构。

干净,最小的解决方案: 如果您使用的是导航图范围视图模型:

替换此内容:

val vm: SomeViewModel by hiltNavGraphViewModels(R.id.nav_vm_id)

与下面:

val vm by activityViewModels<SomeViewModel>()

这允许我将此 VM 用作活动和这些片段之间的共享视图模型。

否则,即使是实时数据观察器也无法工作,因为它会创建彼此独立的新实例和生命周期。

最新更新