Surviving Configuration Change - ViewModel
Android apps are Java programs that run on a special runtime - ART, itself running on top of a linux kernel. As such, they run in a process, have an entry point (main method), threads, resources, native code calls via JNI etc. I enjoy digging around to find out how 'things' are implemented and invoked. In this article, I trace how the
ViewModel is created and survives configuration change.
It is an architectural component that prepares and manages data for the view e.g. a fragment. We can thus use it to invoke business logic calls such as updating user profile or calculating the distance to Jupiter.
As we already know, the current activity instance only gets a reference to a view model but doesn't actually instantiate it. So where does it come from?
This class creates a view model for a particular activity and maintains it as long as the activity is alive. We can create an instance of this class with the following call:
val provider = ViewModelProvider(this)
And then subsequently ask for a view model using:
val viewModel = provider.get(ViewModel::class.java)
Everything is set and we can happily use our view model instance resting assured if we repeat the above call we'll get the same instance as long as our view e.g. activity is not finished.
The API discussed thus far is sufficient for you to use
ViewModels in your application.
What follows are implementation details -- which of course can change anytime.
If an activity owns the
ViewModelProvider, and the ViewModelProvider creates and maintains a view model for us, that instance should be garbage collected if the activity were to be killed from a configuration change, such as a rotation. So how come the view model survives this process? There must be more. There's something that's surviving the configuration change of the activity. The application class? 🤔Could be. Let's check again where the
ViewModelProvider stores the view model instance it makes.
ViewModelStore is the actual storage of the view models. Internally, it uses a table where these instances are stored in referenced through a key. We had the impression that the
ViewModelProvider creates and stores the view models. The creation part is correct, but it uses the ViewModelStore for storage and keeps a reference to the store. Subsequent retrievals are delegated to this store reference.
But still, if the activity is torn down from a configuration change, we'll lose the provider, store and view model references. Nevertheless, the framework ensures that the view model, through the store instance, survives configuration changes.
Back to this:
val provider = ViewModelProvider(this)
Carefully analyzing the
this parameter, we find that its type is a
this is our activity instance, thus an activity is a store owner! It implements the store owner SAM interface to provide a store. It's the responsibility of the store owner to retain the store during configuration changes.
Navigating up the activity hierarchy, we reach the
ComponentActivity which is the implementation of the
This class encloses a static final class
NonConfigurationInstances. This class has 2 fields:
- Object -- custom non-config storage
- ViewModelStore -- our store
ComponentActivity returns an instance of this class when the system calls onRetainNonConfigurationInstance -- called when an activity configuration change happens (see retainNonConfigurationInstances method for extra info). It's usage is disapproved, I presume to allow the framework developers to use it for features such as what we're discussing here.
When the new instance of the activity is later created with the requested configuration, the system gives us the object saved through getLastNonConfigurationInstance. As you remember, the ComponentAcivity had passed an instance of NonConfigurationInstances class that had a reference of our view model store. We can therefore, in
onCreate, retrieve the view model we had previously created.
NB: It is important to note that the
ViewModelStoreOwnerrepresents a scope that maintains a store with
ViewModelsin it. Once this scope is destroyed, for example when the Activity is finished, the store and the view models contained therein are destroyed as well.
onClearedis called in the view model to help you do clean up before the view model is gone.
I went out on a limb to create a high level class diagram for the explanation above (I omitted fields and methods non-essential to our discussion)
This architectural component by the folks at Android is a welcomed addition to apps development. It fits well in the MVVM presentation pattern and can make application architecture a breeze.
We didn't cover how the view model factory fits into all this dance but that shouldn't be hard to trace.
Hopefully this sheds some light on how the view model is created, stored and survives configuration changes. Happy coding!