Previously, we created a centralized ViewModelFactory which can create all ViewModels in the app:
class ViewModelFactory @Inject constructor( private val myViewModelProvider: Provider<MyViewModel>, private val myViewModel2Provider: Provider<MyViewModel2> ): ViewModelProvider.Factory { override fun <T : ViewModel?> create(modelClass: Class<T>): T { return when(modelClass) { MyViewModel::class.java -> myViewModelProvider.get() as T MyViewModel2::class.java -> myViewModel2Provider.get() as T else -> throw RuntimeException("unsupported ViewModel type: $modelClass") } } } But with more and more ViewModels being added to our app, this class will grow quickly, is there a way to mitigate this?
It is a bit cumbersome to create a Factory for each ViewModel we have. Actually, we can just create one Factory that is responsible for creating all ViewModels in the app.
The Factory is very straightforward:
class ViewModelFactory @Inject constructor( private val myViewModelProvider: Provider<MyViewModel>, private val myViewModel2Provider: Provider<MyViewModel2> ): ViewModelProvider.Factory { override fun <T : ViewModel?> create(modelClass: Class<T>): T { return when(modelClass) { MyViewModel::class.java -> myViewModelProvider.get() as T MyViewModel2::class.java -> myViewModel2Provider.
The main advantage of using ViewModel is: it can survive configuration change. That means if you associate a ViewModel with an Activity, then after configuration change, the activity will get the same instance of that ViewModel.
Of course that comes with some setup work to do on developers side. But it is not that bad, basically the activity gets its ViewModel through ViewModelProvider.
If your ViewModel has empty constructor, then you can simply use:
Let’s take a look at the ViewMvcFactory:
class ViewMvcFactory @Inject constructor( private val layoutInflaterProvider: LayoutInflater, private val imageLoaderProvider: ImageLoader ) One thing worth paying attention is that this is a factory. Factory is used to create objects. So this means that its dependencies: LayoutInflater and ImageLoader will be reused to create objects everytime.
We know that ImageLoader has no state so that it can be reused, but what if it can’t?
This is a simple one. No more explanation, just paste the code here.
@UiThread @Module class AppModule { @Provides @AppScope @Retrofit1 fun retrofit1(urlProvider: UrlProvider): Retrofit { return Retrofit.Builder() .baseUrl(urlProvider.baseUrl1()) .addConverterFactory(GsonConverterFactory.create()) .build() } @Provides @AppScope @Retrofit2 fun retrofit2(): Retrofit { return Retrofit.Builder() .baseUrl(urlProvider().baseUrl2()) .addConverterFactory(GsonConverterFactory.create()) .build() } @Provides @AppScope fun stackOverflowApi(@Retrofit1 retrofit: Retrofit): StackoverflowApi = retrofit.create(StackoverflowApi::class.java) @Provides @AppScope fun urlProvider(): UrlProvider = UrlProvider() } @Qualifier annotation class Retrofit1() @Qualifier annotation class Retrofit2()