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 共享同一個類型的實例。

相關文章
相關標籤/搜索