I'm transitioning my app to use ViewModel
s. For instantiating the ViewModel
s I use a custom Factory
and Dagger 2 for dependency injection. It looks like this:
@Singleton
class ViewModelFactory @Inject constructor(
private val viewModels: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T
= viewModels[modelClass]!!.get() as T
}
@Target(AnnotationTarget.FUNCTION) @Retention(AnnotationRetention.RUNTIME) @MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)
@Module
abstract class ViewModelModule {
@Binds
abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
@Binds @IntoMap @ViewModelKey(FooViewModel::class)
abstract fun fooViewModel(viewModel: FooViewModel): ViewModel
}
Now I'm facing the question, how to access the Arguments/Extras Bundle
of my Fragment
or Activity
inside a ViewModel
. This is necessary because the views and therefore the data in the ViewModel
is often parametrizable. How would I implement this using the least amount of boilerplate?
We only need to read from a saved Bundle when the ViewModel gets created, not when it is reused. The Activity or Fragment does not know this, so it is the wrong class to add this to. But there is the ViewModelProvider. Factory that creates the ViewModel.
Setup a Dagger 2 Module Then setup the Dagger 2 Module with that provide the ViewModel. Like normal ViewModel creation, use ViewModelProvider and the Factory to create it. We'll need to pass in the dependencies which can be automatically injected by Dagger 2 using the conventional approach.
The koin-android Gradle module introduces a new viewModel DSL keyword that comes in complement of single and factory , to help declare a ViewModel component and bind it to an Android Component lifecycle. Your declared component must at least extends the android.
The viewModels() extension function is provided by androidx KTX . Even if the ViewModel has savedStateHandle or is taking the intent bundle from the Activity, it doesn't need to be explicitly injected in. The viewModels() extension function does all that automatically behind the scene.
If you use Saved State module, both activity intent extras and fragment arguments are available via SavedStateHandle
class SavedStateViewModel(private val state: SavedStateHandle) : ViewModel() {
init {
val someArgument = state.get<String>("someArgumentKey")
...
}
}
You can also use AssistedInject library to pass arguments from an Activity/Fragment into a ViewModel, but it's a boilerplate solution. Here's a dagger example, and another one which uses dagger hilt.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With