Update: With the release of Hilt, this article is no longer useful as a guide and the library has been deprecated. For more information read my article about Hilt.
Nimrod Dayan wrote a great article on Saving UI state with ViewModel SavedState and Dagger. He goes through and demonstrates how to use Dagger, ViewModels, and Saved State module for ViewModel together with AssistedInject. However, we are left with a large amount of boilerplate by the end.
Something that is repetitive that can be automated.
Jake Wharton— Helping Dagger Help You @ 1:52
Half way through the presentation Jake approaches the topic of view injection using AssistedInject. He gets down to the Factory and Module boilerplate, similar to above, and says to use code generation to handle it. Jake then presents two new annotations to help with this @InflationInject and @InflationModule.
Can we use the same tricks @ViewModelInject and @ViewModelModule to take care of the ViewModel boilderplate? Yes, yes we can!
Unfortunately¹ there is a challenge to making this a reality:
Are there any standard things that should be assisted into a ViewModel and can a library adequately define that list as to be usable in general?
I believe the answer is yes and SavedStateHandle is that thing.
SavedStateHandle handles, as the name says, saved state. So when your app is killed by the system to free up resources it can present that state back to the ViewModel when the user comes back. SavedStateHandle also takes care of initial arguments/parameters like ids and other things passed to the activity or fragment in the intent and bundle arguments respectively. The AbstractSavedStateVMFactory has a constructor that takes default arguments in the form of a bundle. With that we can directly pass in the intent extras or fragment args through the SavedStateHandle to the ViewModel.
One could claim that the application context could be assisted since the Jetpack developers created an AndroidViewModelFactory. The Application context is a “single, global Application object of the current process” so this can safely² be included in a module and injected like any other dependency.
With that opinion in mind I wrote the ViewModelInject library to take care of the boilerplate. So now you can do full dagger injection with SavedStateHandle without the repetitive and automate-able boilerplate.
¹ In my opinion this is fortunate. With out challenges this wouldn’t be fun.
² My dagger graph is tied to my application context lifecycle.