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()
Let’s create a ScreensNavigator interface:
interface ScreensNavigator { fun navigateBack() fun toQuestionDetails(questionId: String) } And ScreensNavigatorImpl implements it:
class ScreensNavigatorImpl @Inject constructor(private val activity: AppCompatActivity) : ScreensNavigator { override fun navigateBack() { activity.onBackPressed() } override fun toQuestionDetails(questionId: String) { QuestionDetailsActivity.start(activity, questionId) } } Now we have a problem that in activities and fragments we want to inject the interface type:
@Inject lateinit var screensNavigator: ScreensNavigator But Dagger only knows how to create ScreensNavigatorImpl.