Reading Time: 5 minutes

In the name of God

Hi guys, welcome back to my blog.

One of the good components provided by Jetpack is ViewModel. ViewModel is around for some time now and I can confidently say almost every Android developer has heard about it at least once. Long story short, ViewModel allows us to save and restore objects when a configuration change happens. This post is not just another tutorial on how to import/use ViewModel. I assume you already know the basics of it. Otherwise, The documentation is pretty good these days to get you started.

One thing that got us struggling from day one of using ViewModel was the way we have to create it. Since the instance of ViewModel has to survive config changes you can not simply create a new object using its constructor. The standard way of ViewModel creation is through ViewModelProvider and ViewModelFactory. With the help of Kotlin, they added property delegates to ease the creation of ViewModel.


This is not that bad.

Now let’s step into Dagger world of DI. With Dagger, we want to move this creation logic to Modules. But with ViewModels it’s not really straightforward. I am going to just list a few approaches I used before to provide ViewModel using Dagger:

  1. Inject your ViewModel Factory.
  2. Using Dagger MultiBinding and a map of all the ViewModels.
  3. Binding your Activity/Fragment into the graph (@BindsInstance) and provide final ViewModel.

These approaches really help and do a good job with DI. But I think we can do better. We can remove this boilerplate once and for all.

Let’s see if we can directly inject our ViewModels into our Views without worrying about Factory/ViewModelProvider.

First try: Removing Factory

Since we expect our ViewModels to have a parameter in their constructor, all of them need a factory to be created. But this factory doesn’t need to be unique for each of them. So we can create a single factory and provide any ViewModel we want. But we want to inject our ViewModels not to create them. For that, we can reference Dagger Component and ask for a ViewModel directly. With the help of ViewModelLazy from ViewModel-ktx we can glue all of them together.

Let’s see how the final code will look like:


Now let’s go through the code to see what’s going on here:


We need a property to store the ViewModel instance in our view. This immediately means they should not use the ViewModel object that our component injects directly and use this instead. ViewModelLazy comes to help here to simplifies the lazy creation of viewModels.


This small class allows us to create any ViewModel that provideViewModel() returns us. As provideViewModel() is an abstract function developers have to implement it and return the ViewModel provided by Dagger component.

Sample usage of ViewModelFragment will be like:


As you can see neither the Dagger Component nor us do not have to deal with the ViewModel creation.

Important notes here is that:

  • Do not use injected ViewModel directly, return it in provideViewModel() instead.
  • Since our Dagger Component is still recreated every time, we can provide dependencies that are bound to the fragment lifecycle like BroadCastReceivers.
  • Every time we call component.viewModel() Dagger creates a new instance of our ViewModel. So we should be careful not two call it more than once. ViewModelProvider will take care of that for us.
Worth mentioning:
We can achieve the same thing with a delegate or an extension function on the fragment. This way we don’t force developers to extend from our ViewModelFragment.

Second try: Surviving Config Changes with Dagger components

What if we could have our Dagger Components survive Configuration Changes from the beginning. This way we could add Component Scope to our ViewModels and Dagger will take care of generating that ViewModel only once per screen. But wait, that means we don’t even need the ArchViewModel any more. Even better this opens our hand to use any type of “Presenter” like class we want and easily hook it with our fragments.

To save our Components through fragment lifecycle we have two options:

  1. Retained Fragments
  2. ViewModels(!)

I’m not a fan of Retained Fragments so I went with ViewModels again. But didn’t we just see all the problems they have with creation just now? Yeah, yet this time is different.

Again let’s see what the final code would look like first:


Break down:


This time we need to hold the Dagger Component instance. Also using this parameter we ensure that our component survives the config changes. The trick here is that we wrap the parameter instance (component) inside a ViewModel. The Lazy delegate here will block us from retrieving the StoreViewModel every time we access component.


This is a simple ViewModel and has only one job: Keeping the component safe while we are going through the config changes.

A simple usage of this StoreFragment would look like this:


The SecondViewModel is a simple class and does not extend from ArchitectureViewModel. Again to avoid recreation of our ViewModel every time we call inject we have to scope our SecondViewModel.

Like our last try, we could achieve the same effect using an extension function this way we can avoid extending a StoreFragment.

Important notes with this approach are:

  • Entire DaggerComponent will survive, So we can not reference the Fragment/Activity in the graph nor bind it.
  • We can not access or provide anything related to the views.
  • Since we are not using the Architecture ViewModels here we don’t have the onCleared() call back in our “Presenters”. There are ways to replicate that call back as well.
  • I like this approach more because it feels like more generic and more platform-agnostic to me.

Bonus:

Everything we talked about here was mostly based on ViewModels so you can apply the concepts to Fragments or Activities.

On top of these, In Activities, we have two callbacks that we can make use of to totally ditch the ViewModel once and for all. If you are using pure framework Activity there is onRetainNonConfigurationInstance() function and in case of AppCompatActivity you can use onRetainCustomNonConfigurationInstance(). As the name suggests any object you return from these functions will survive the config changes. There are corresponding get functions for each of them.

Spoiler Alert: New ViewModel uses onRetainNonConfigurationInstance() under the hood.

In this post, we saw some ways to remove the hard dependency on ViewModels and instead rely on pure Kotlin classes. There are pros and cons in these ways. One of the reasons I like to avoid ViewModels is to help get closer to the multiplatform world as well as managing every object creation using Dagger. But obviously this is not a silver bullet and will not work for everyone.

I hope this be of help. You can find a full sample code on Github.

Thanks for reading.

LEAVE A REPLY

Please enter your comment!
Please enter your name here