Hilt Dependency Injection
Hilt is a dependency injection framework built on top of Dagger that helps reduce a lot of boiler plate when injecting dependencies. Hilt can also help reduce the boiler plate when using SavedStateFlow by scoping an instance of SavedStateFlowHandle to any ViewModel that requests it.
Hilt ViewModel
The saved-state-flow-hilt artifact provides an instance of SavedStateFlowHandle to the ViewModelComponent out of the box, so it can be declared in any ViewModel constructor like so.
@HiltViewModel
class MainViewModel @Inject constructor(
savedStateFlowHandle: SavedStateFlowHandle,
private val newsDataSource: NewsDataSource
) : ViewModel() {
private val query: SavedStateFlow<String> =
savedStateFlowHandle.getSavedStateFlow(viewModelScope, "main-viewmodel-query-key", "")
init {
observeQuery()
}
fun updateQuery(query: String) {
this.query.value = query
}
private fun observeQuery() {
viewModelScope.launch {
query.asStateFlow()
.flatMapLatest { query ->
newsDataSource.fetchQuery(query)
}
.collect { results ->
// TODO update state with latest results
}
}
}
}
The full version of this ViewModel can be found here.
Grabbing a Reference to @HiltViewModel
Finally grab a reference to the ViewModel using the by viewmodels { } delegation function.
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
private val viewModel: MainViewModel by viewModels()
}
Assisted Injection
When using Hilt, it's possible that the @HiltViewModel annotation cannot be used when a value needs to be injected into the constructor at runtime. For that, there are a couple of extension methods provided to help inject a SavedStateFlowHandle when using Hilt's assisted injection from a FragmentActivity or Fragment.
Assisted Injected ViewModel
class MyAssistedViewModel @AssistedInject constructor(
@Assisted savedStateFlowHandle: SavedStateFlowHandle,
@Assisted id: String
) : ViewModel() {
@AssistedFactory
interface Factory {
fun create(savedStateFlowHandle: SavedStateFlowHandle, id: String): MyAssistedViewModel
}
}
Grab a reference to an Assisted ViewModel
Then in a Fragment or a FragmentActivity, the by assistedViewModel method may be used to get a reference to a assisted injected ViewModel as this method provides you an instance of a SavedStateFlowHandle. There is also a method for fragments to get a ViewModel scoped to its FragmentActivity if using the by assistedActivityViewModel {} method.
@AndroidEntryPoint
class AssistedFragment : Fragment() {
@Inject
lateinit var factory: MyAssistedViewModel.Factory
private val viewModel: MyAssistedViewModel by assistedViewModel { savedStateFlowHandle ->
factory.create(savedStateFlowHandle, arguments?.getString("some-argument-key")!!)
}
}