Hilt 在同一个 activity 中创建不同的视图模型实例

Hilt creating different instances of view model inside same activity

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


@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()}")
    }
}

hashCode的值在所有片段中不一致。我无法弄清楚在 activity.

中生成 viewmodel 的单例实例还缺少什么

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

当您使用 by viewModels 时,您正在创建一个作用域为该单个 Fragment 的 ViewModel - 这意味着每个 Fragment 都将拥有该 ViewModel class 的单独实例。如果您想要将单个 ViewModel 实例的范围限定为整个 Activity,您需要使用 by activityViewModels

private val homeViewModel by activityViewModels<HomeViewModel>()

Ian说的对,by viewModels是Fragment的扩展函数,会把Fragment作为ViewModelStoreOwner

如果您需要将其限定在 Activity 范围内,您可以使用 by activityViewModels

但是,您通常不需要 Activity 范围的 ViewModel。它们在单个 Activity 应用程序中实际上是全局的。

要创建 Activity-global 非状态组件,您可以使用 Hilt 中的 @ActivityRetainedScope。这些将可用于在 Activity 或 Fragment.

中创建的 ViewModel

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

很有可能在这一点上,您真正想要的不是 Activity 范围的 ViewModel,而是 NavGraph 范围的 ViewModel。

要通过 Hilt 的 @Assisted 注释将 SavedStateHandle 放入 Fragment 内的 NavGraph 范围的 ViewModel 中,您可以(编辑:不能)使用:

//@Deprecated
//inline fun <reified T : ViewModel> Fragment.hiltNavGraphViewModels(@IdRes navGraphIdRes: Int) =
//viewModels<T>(
//    ownerProducer = { findNavController().getBackStackEntry(navGraphIdRes) },
//    factoryProducer = { defaultViewModelProviderFactory }
//)

.

编辑:由于https://github.com/google/dagger/issues/2152上述方法不起作用,所以可以使用访问器并直接使用访问器构建 NavGraph 范围的 AbstractSavedStateViewModelFactory .目前有点乱,因为ActivityRetainedComponent访问困难,敬请期待更好的解决方案...

这是 ianhanniballake 提到的替代解决方案。它允许您在片段之间共享一个视图模型,而不会将其分配给 activity,因此您可以避免像 EpicPandaForce 所述那样在单个 activity 中创建本质上的全局视图模型。如果您使用的是导航组件,则可以创建要共享视图模型的片段的嵌套导航图(遵循本指南:Nested navigation graphs

每个片段内:

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

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