乐趣区

关于android:Hilt-新组件-ViewModelComponent

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

ViewModelComponent 增加到 Hilt 之前,ViewModel 类通过 ActivityRetainedComponent创立和注入。因而,ViewModel 中的依赖项仅能够应用未限定作用域、或是将作用域限定到 SingletonComponentActivityRetainedComponent 中,被所有 ViewModel 共享实例的类型。

如果您的 App 每个页面都仅为一个 Activity,上述内容并不会成为问题,因为此状况中将类型的作用域限定为 ActivityRetainedComponent 意味着每个页面的 ViewModel 类都将取得该类型的不同实例。然而,每个页面仅为一个 Activity 并不适用于大多数 App。

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

当初,您能够通过遵循 ViewModel 生命周期的 ViewModelComponent 组件来创立并注入 ViewModel。每一个 ViewModel 实例持有不同的 ViewModelComponent 实例,您能够应用 @ViewModelScoped 注解,将类型的作用域限定到该组件上。

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

ViewModelComponent 继承自 ActivityRetainedComponent,因而它的类型限定依赖于下层的 SingletonComponentActivityRetainedComponent。除此之外,ViewModelComponent 还默认绑定了一个与 ViewModel 关联的 SavedStateHandle

将作用域绑定为 ViewModelComponent

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

然而,因为 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 共享同一个类型的实例。

退出移动版