Hilt 新组件 | ViewModelComponent

ViewModelComponent 是一个 Hilt 组件层次结构 (Component hierarchy) 中的一员,它遵循 ViewModel 的生命周期,并能够限定类型的做用域到此组件上。android

ViewModelComponent 添加到 Hilt 以前,ViewModel 类经过 ActivityRetainedComponent建立和注入。所以,ViewModel 中的依赖项仅可使用未限定做用域、或是将做用域限定到 SingletonComponentActivityRetainedComponent 中,被全部 ViewModel 共享实例的类型。缓存

若是您的 App 每一个页面都仅为一个 Activity,上述内容并不会成为问题,由于此状况中将类型的做用域限定为 ActivityRetainedComponent 意味着每一个页面的 ViewModel 类都将得到该类型的不一样实例。然而,每一个页面仅为一个 Activity 并不适用于大多数 App。ide

此外,ActivityRetainedComponent 组件不会默认绑定 SavedStateHandlegoogle

如今,您能够经过遵循 ViewModel 生命周期的 ViewModelComponent 组件来建立并注入 ViewModel。每个 ViewModel 实例持有不一样的 ViewModelComponent 实例,您可使用 @ViewModelScoped 注解,将类型的做用域限定到该组件上。spa

ViewModelComponent 在精简版 Hilt 组件层次结构中的位置

ViewModelComponent 继承自 ActivityRetainedComponent,所以它的类型限定依赖于上层的 SingletonComponentActivityRetainedComponent。除此以外,ViewModelComponent 还默认绑定了一个与 ViewModel 关联的 SavedStateHandlecode

将做用域绑定为 ViewModelComponent

与其余组件相比,经过使用 @ViewModelScoped 将做用域绑定为 ViewModelComponent,并将其注入到 ViewModel 中,能够得到更好的灵活性和更精细的控制粒度。ViewModel 能够在配置更改中保存状态,而且其生命周期能够被 Activity、Fragment,甚至是 导航图 控制。component

可是,因为 ActivityComponentFragmentComponent 不会在配置更改中保存状态,因此在某些状况下仍然有必要限定做用域到这些组件。另外,FragmentComponent 继承自 ActivityComponent,使用多个 ViewModelComponent 没法实现相同的行为。继承

所以:接口

  • 若是须要全部的 ViewModel 共享同一个类型的实例,使用 @ActivityRetainedScoped 注解。
  • 若是须要将类型的做用域限定为 ViewModel,使其在配置更改时保留状态,或使其受导航图控制,使用 @ViewModelScoped 注解。
  • 若是须要将类型的做用域限定为 Activity,而且不但愿在配置更改时保留状态,使用 @ActivityScoped 注解,若是须要将做用域限定为 Fragment 并实现上述行为,使用 @FragmentScoped 注解。

使用 @ViewModelScoped

您可使用该注解将一个类型的做用域限定为特定 ViewModel 的实例。ViewModel 及其依赖项以及他们的依赖都将注入相同的实例。生命周期

下面的示例中,LoginViewModel 以及 RegistrationViewModel 分别使用了被 @ViewModelScoped 注解的 UserInputAuthData 类型,使它们拥有不一样的状态。

@ViewModelScoped // 将类型的做用域限定为 ViewModel
class UserInputAuthData(
  private val handle: SavedStateHandle //在 ViewModelComponent 中默认绑定
) { /* 逻辑代码以及缓存数据*/ }

class RegistrationViewModel(
  private val userInputAuthData: UserInputAuthData,
  private val validateUsernameUseCase: ValidateUsernameUseCase,
  private val validatePasswordUseCase: ValidatePasswordUseCase
) : ViewModel() { /* ... */ }

class LoginViewModel(
  private val userInputAuthData: UserInputAuthData,
  private val validateUsernameUseCase: ValidateUsernameUseCase,
  private val validatePasswordUseCase: ValidatePasswordUseCase
) : ViewModel() { /* ... */ }

class ValidateUsernameUseCase(
  private val userInputAuthData: UserInputAuthData,
  private val repository: UserRepository
) { /* ... */ }

class ValidatePasswordUseCase(
  private val userInputAuthData: UserInputAuthData,
  private val repository: UserRepository
) { /* ... */ }

由于 UserInputAuthData 的做用域被限定为 ViewModel,RegistrationViewModelLoginViewModel 将得到不一样的 UserInputAuthData 实例。然而,每一个 ViewModel 中没有限定做用域的 UseCase 依赖会与其 ViewModel 使用相同的 UserInputAuthData 实例。

向 ViewModelComponent 中添加绑定

和其余组件同样,您能够向 ViewModelComponent 中添加绑定。若是在上述代码片断中,ValidateUsernameUseCase 是一个接口,您能够这样通知 Hilt 使用哪一种实现:

@Module
@InstallIn(ViewModelComponent::class)
object UserAuthModule {

  @Provides
  fun provideValidateUsernameUseCase(
    userInputAuthData: UserInputAuthData,  //做用域为 ViewModelComponent
    repository: UserRepository
  ): ValidateUsernameUseCase {
    return ValidateUsernameUseCaseImpl(userInputAuthData, repository)
  }
}

ViewModelComponent 遵循 ViewModel 的生命周期,并能够将类型的做用域限定到此组件上。因为 ViewModel 的生命周期能够被 Activity、Fragment 甚至是 导航图 所控制,您能够根据须要将做用域限定到这些地方,来得到更大的灵活性和更精细的控制粒度。

请使用 @ViewModelScoped 将类型的做用域限定为 ViewModel。使用 @ActivityRetainedScoped 限定做用域,使同一界面的全部的 ViewModel 共享同一个类型的实例。

相关文章
相关标签/搜索