Almost all tutorial or article that I read always instantiate ViewModel
in Activity or Fragment class, however is it okay to create a module to provide ViewModel
thus instantiation can be skipped?
Using the popular solution for Dagger 2 and ViewModel
/**
* A custom ViewModelProvider.Factory which also allow us to create ViewModel with argument on its constructor.
* Facilitates ViewModel injection, checking of ViewModelKey existence is done on runtime.
* @throws IllegalArgumentException when a ViewModel does not exist, possible due to missing ViewModelKey
* */
@Singleton
class ViewModelProviderFactory @Inject constructor(
private val creators: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
val creator = creators[modelClass] ?: creators.entries.firstOrNull {
modelClass.isAssignableFrom(it.key)
}?.value
?: throw IllegalArgumentException("Unknown ViewModel $modelClass. Forgot to provide and map the ViewModelKey for this ViewModel?")
@Suppress("UNCHECKED_CAST")
return creator.get() as T
}
}
Then a ViewModel Factory module
/**
* This module provides a custom ViewModelProvider.Factory.
* @see ViewModelProviderFactory
* */
@Module(
includes = [
VideosViewModelModule::class
]
)
abstract class ViewModelFactoryModule {
@Binds
abstract fun bindViewModelFactory(viewModelProviderFactory: ViewModelProviderFactory): ViewModelProvider.Factory
}
Sample ViewModel provider module
@Module
object VideosViewModelModule {
@Provides
@IntoMap
@ViewModelKey(VideoViewModel::class)
fun provideVideoViewModel(
useCase: GetVideosUseCase
): ViewModel = VideoViewModel(useCase)
@Provides
fun provideVideoViewModelProvider(
owner: VideosFragment,
factory: ViewModelProvider.Factory
) = ViewModelProvider(owner, factory)[VideoViewModel::class.java]
}
With this approach I can easily inject the VideoViewModel
to VideosFragment
and use it immediately without instantiating it inside onCreate
or onCreateView
, but I wonder if this is okay as I cannot find yet an article that uses this approach.
Note that VideosFragment
extends DaggerFragment
and is already annotated with @ContributesAndroidInjector
in a module.