My experience in MVVM — using ViewModel from Architecture Components
Has been a while since the first release of Architecture Components, but the topic it’s still hot and ViewModel (VM) has been widely discussing and using — at least at the company I work for.
Beside basics of MVVM using Architecture Components, also a sample app is presented below.
The Basics
It doesn’t seem to be a very big difference between MVP and MVVM. The most significant difference I see is that VM — unlike the Presenter — doesn’t have the View component as a dependency, however exposes the data changes which creates the connection from the VM toward View.
The MVVM architecture is quite simple: the View component creates the VM and initiates actions defined in it. On the other side VM defines and exposes LiveData objects in order to send data to the View, which subscribes to data changes thus observing them.
The Details
The View component is responsible for creating the VM — being an Activity, Fragment or simply any other View object— thus the View component has a reference to the VM. This is done by using ViewModelProviders#of(Fragment) or ViewModelProviders#of(FragmentActivity). Furthermore if the VM defines parameters in it’s constructor, implementing ViewModelProvider.Factory can be helpful passing dependencies from View toward VM.
By having the VM instance, the View now is able to invoke defined actions (methods) of it.
In the most widely used cases the VM is responsible for preparing and storing the data for the View. Therefore by using interactors, managers, use cases, repositories gets the data and wraps it in LiveData objects.
Simply put LiveData is a data wrapper class, which is observable within a Lifecycle of Activity, Fragment, meaning that the observers are going to be notified only when the Activity or Fragment it’s active.
When data is ready on the VM side, it’s time to wrap it in LiveData object using either MutableLiveData#setValue(Object) or MutableLiveData#postValue(Object). Since LiveData does not expose publicly setValue(Object) and postValue(Object), MutableLiveData comes for help by overriding these methods using the public access modifier.
After creating getter method for LiveData in VM, the View is able to observe the changes using LiveData#observe(LifecycleOwner, Observer). This could look this way:
viewModel.getLoginLiveData().observe(this, new Observer<Boolean>() {
@Override
public void onChanged(Boolean isSuccess) {
}
});
The Benefits
Implementing a provider and caching mechanism for the Presenter to survive configuration changes it’s a considerable effort. For example Loader can be used, but anyway it takes some time to implement. Unlike the Presenter, VM is automatically retained on configuration changes with the help of ViewModelProviders, and finished only when Activity finishes or Fragment gets detached without saving state.
Another important benefit is that LiveData observers are going to be called only when it’s in active state. This active state can be defined based whether the LiveData has subscribed observers or not. When the lifecycle of Activity or Fragment enters the DESTROYED state, the observer will be removed from the LiveData. This way the Activity won’t be updated when it’s not started (meaning Activity#onStart() was not called yet, or Activity#onPause() was dispatched) — no memory leaks, LiveData does not hold a reference to the Activity in this state. I remember those ugly view!=null checks in the Presenter at each line where the view instance was referenced — LiveData handles this out of the box.
Going further the LiveData also dispatches the latest value of it’s data immediately after the observer’s Lifecycle gets again in the active state — this way data changes applied to the LiveData in the inactive state won’t be lost.
Beneficial use cases
Beside the ordinary use case where the View component passes the business logic to VM, there are other scenarios where it’s useful.
One case is when two fragments live in the same Activity and they have to communicate with each other. They could share the same VM by using the same owner Activity reference for getting VM from ViewModelProviders.
Also could be helpful simply for handling configuration changes without business logic in VM: the class instance responsible for getting the data (repository, interactor, use case) is wrapped in a ViewModel and triggering data retrieval only at VM initialisation, not on every configuration change.
Using LiveData, a system service could be wrapped into a singleton LiveData subclass and based on whether it has observers or not, the system service should be started or stopped — thus minimalizing resource and data usage which, for example in the case of LocationManager could be critical.
The POC
I’ve created a sample app which aims to demonstrate the usage of VM from Architecture Components. Simply put it takes orders (simple ID for the moment) from users (i.e. waiters) and retrieves a list of Order from data service classes.
Google’s implementation of ViewModel and others in Architecture Components can save a lot of effort regarding to the separation of the View component from business logic and more. Even if implementing MVVM isn’t in scope, these components could be used separately, like I mentioned above in the benefits section.
Of course there are tons of useful resources, specifically I recommend the robust one from Florina Muntenescu (even if does not use Architecture Components), or this one related to some implementation tips.