ViewModelComponent
是一个 Hilt 组件层次结构 (Component hierarchy) 中的一员,它遵循 ViewModel 的生命周期,并能够限定类型的作用域到此组件上。
在 ViewModelComponent
增加到 Hilt 之前,ViewModel 类通过 ActivityRetainedComponent
创立和注入。因而,ViewModel 中的依赖项仅能够应用未限定作用域、或是将作用域限定到 SingletonComponent
或 ActivityRetainedComponent
中,被所有 ViewModel 共享实例的类型。
如果您的 App 每个页面都仅为一个 Activity,上述内容并不会成为问题,因为此状况中将类型的作用域限定为 ActivityRetainedComponent
意味着每个页面的 ViewModel 类都将取得该类型的不同实例。然而,每个页面仅为一个 Activity 并不适用于大多数 App。
此外,ActivityRetainedComponent
组件不会默认绑定 SavedStateHandle
。
当初,您能够通过遵循 ViewModel 生命周期的 ViewModelComponent 组件来创立并注入 ViewModel。每一个 ViewModel 实例持有不同的 ViewModelComponent 实例,您能够应用 @ViewModelScoped 注解,将类型的作用域限定到该组件上。
ViewModelComponent 在精简版 Hilt 组件层次结构中的地位
ViewModelComponent
继承自 ActivityRetainedComponent
,因而它的类型限定依赖于下层的 SingletonComponent
和 ActivityRetainedComponent
。除此之外,ViewModelComponent 还默认绑定了一个与 ViewModel 关联的 SavedStateHandle
。
将作用域绑定为 ViewModelComponent
与其余组件相比,通过应用 @ViewModelScoped 将作用域绑定为 ViewModelComponent,并将其注入到 ViewModel 中,能够取得更好的灵活性和更精密的管制粒度。ViewModel 能够在配置更改中保留状态,并且其生命周期能够被 Activity、Fragment,甚至是 导航图 管制。
然而,因为 ActivityComponent
和 FragmentComponent
不会在配置更改中保留状态,所以在某些状况下依然有必要限定作用域到这些组件。另外,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,RegistrationViewModel
和 LoginViewModel
将取得 不同的 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 共享同一个类型的实例。