1

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.

Bitwise DEVS
  • 2,858
  • 4
  • 24
  • 67

0 Answers0