ViewModelComponent
是一個 Hilt 組件層次結構 (Component hierarchy) 中的一員,它遵循 ViewModel 的生命週期,並能夠限定類型的做用域到此組件上。android
在 ViewModelComponent
添加到 Hilt 以前,ViewModel 類經過 ActivityRetainedComponent
建立和注入。所以,ViewModel 中的依賴項僅可使用未限定做用域、或是將做用域限定到 SingletonComponent
或 ActivityRetainedComponent
中,被全部 ViewModel 共享實例的類型。緩存
若是您的 App 每一個頁面都僅爲一個 Activity,上述內容並不會成爲問題,由於此狀況中將類型的做用域限定爲 ActivityRetainedComponent
意味着每一個頁面的 ViewModel 類都將得到該類型的不一樣實例。然而,每一個頁面僅爲一個 Activity 並不適用於大多數 App。ide
此外,ActivityRetainedComponent
組件不會默認綁定 SavedStateHandle
。google
如今,您能夠經過遵循 ViewModel 生命週期的 ViewModelComponent 組件來建立並注入 ViewModel。每個 ViewModel 實例持有不一樣的 ViewModelComponent 實例,您可使用 @ViewModelScoped 註解,將類型的做用域限定到該組件上。spa
ViewModelComponent 在精簡版 Hilt 組件層次結構中的位置
ViewModelComponent
繼承自 ActivityRetainedComponent
,所以它的類型限定依賴於上層的 SingletonComponent
和 ActivityRetainedComponent
。除此以外,ViewModelComponent 還默認綁定了一個與 ViewModel 關聯的 SavedStateHandle
。code
與其餘組件相比,經過使用 @ViewModelScoped 將做用域綁定爲 ViewModelComponent,並將其注入到 ViewModel 中,能夠得到更好的靈活性和更精細的控制粒度。ViewModel 能夠在配置更改中保存狀態,而且其生命週期能夠被 Activity、Fragment,甚至是 導航圖 控制。component
可是,因爲 ActivityComponent
和 FragmentComponent
不會在配置更改中保存狀態,因此在某些狀況下仍然有必要限定做用域到這些組件。另外,FragmentComponent
繼承自 ActivityComponent
,使用多個 ViewModelComponent
沒法實現相同的行爲。繼承
所以:接口
@ActivityRetainedScoped
註解。@ViewModelScoped
註解。@ActivityScoped
註解,若是須要將做用域限定爲 Fragment 並實現上述行爲,使用 @FragmentScoped
註解。您可使用該註解將一個類型的做用域限定爲特定 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 中添加綁定。若是在上述代碼片斷中,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 共享同一個類型的實例。