Android + Dagger2
Here is one clean solution of how to combine Dagger + ViewModel + SavedStateHandle.
The only difference now is that inside our ViewModel has access to SavedStateHandle. We capture this through a abstract class:
abstract class SavedStateViewModel: ViewModel() { abstract fun init(savedStateHandle: SavedStateHandle) } And the ViewModel now looks like this:
class MyViewModel @Inject constructor( private val fetchQuestionsUseCase: FetchQuestionsUseCase ) : SavedStateViewModel() { private lateinit var _questions: MutableLiveData<List<Question>> val questions: LiveData<List<Question>> get() = _questions override fun init(savedStateHandle: SavedStateHandle) { _questions = savedStateHandle.
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?