The ViewModel class is designed to store and manage interface-related data in a life-cycle oriented manner. The ViewModel class allows data to persist after configuration changes such as screen rotation
The Android framework manages the lifecycle of interface controllers such as Activities and Fragments. The Android framework may decide to destroy or recreate the interface controller in response to certain user actions or device events that are completely outside your control.
If the system destroys or recreates the interface controller, any transient interface related data stored in it is lost. For example, an application might include a list of users in one of its activities. After recreating the Activity for configuration changes, the new Activity must reextract the user list. For simple data, the Activity can use onSaveInstanceState()
Method recovers its data from the bundle in onCreate(), but this method is only suitable for small amounts of data that can be serialized and deserialized, not potentially large amounts of data such as user lists or bitmaps.
Another problem is that interface controllers often need to make asynchronous calls that may take some time to return. The interface controller needs to manage these calls and ensure that the system cleans them up after they are destroyed to avoid potential memory leaks. This administration requires a lot of maintenance work and, in the case of recreating objects for configuration changes, wastes resources because objects may need to re-issue calls that have already been made.
Interface controllers such as activities and fragments are primarily used to display interface data, respond to user actions, or handle operating system communications (such as permission requests). If the interface controller is also responsible for loading data from the database or network, the class becomes bloated. Assigning too much responsibility to an interface controller can cause a single class to try to handle all the work of the application itself, rather than delegating it to another class. Assigning too much responsibility to the interface controller in this way also makes testing significantly more difficult.
It is easier and more efficient to separate view data ownership from interface controller logic.
To realize the ViewModel
The architecture component provides the ViewModel helper class for the interface controller, which is responsible for preparing the data for the interface. ViewModel objects are automatically retained during configuration changes so that the data they store is immediately available for use by the next Activity or Fragment instance. For example, if you need to display a list of users in your app, make sure you assign the responsibility of getting and keeping that list to the ViewModel, not the Activity or Fragment, as shown in the following example code:
You can then access the list from your Activity, as follows:
If the Activity is recreated, it receives the same MyViewModel instance as the one created by the first Activity. When the owner Activity completes, the framework calls the onCleared() method of the ViewModel object so that it can clean up the resource.
Note: The ViewModel must not reference a view, Lifecycle, or any class that might store a reference to the Activity context.
ViewModel objects live longer than specific instances of views or LifecycleOwners. It also means that you can write tests that cover the ViewModel more easily because it doesn’t understand views and Lifecycle objects. ViewModel objects can include LifecycleObservers, such as a LiveData object. However, ViewModel objects must not observe changes to life-cycle-aware observables such as LiveData objects. If the ViewModel needs an Application Context (for example, to find a system service), it can extend the AndroidViewModel class and set the constructor to receive the Application, because the Application class extends the Context.
ViewModel lifecycle
The ViewModel object exists for a time Lifecycle that is passed to the ViewModelProvider when the ViewModel is acquired. The ViewModel will remain in memory until Lifecycle is gone permanently: for an Activity, when the Activity completes; For fragments, this is when the Fragment is separating.
Figure 1 illustrates the various lifecycle states in which an Activity goes through a screen rotation and then ends. The diagram also shows the ViewModel life cycle next to the associated Activity life cycle. This diagram illustrates the various states of the Activity. These basic states also apply to the Fragment lifecycle.
You typically request the ViewModel the first time the system calls the Activity object’s onCreate() method. The system may call onCreate() several times throughout the Activity’s life cycle, such as while rotating the device screen. The ViewModel exists from the time you first request the ViewModel until the Activity completes and is destroyed.
Share data between fragments
It is common for two or more fragments in an Activity to need to communicate with each other. Imagine a common case of master-detail fragments. Suppose you have a Fragment in which the user selects an item from a list and another Fragment that displays the contents of the selected item. This situation is not easy to handle because both fragments need to define some kind of interface description, and the owner Activity must bind the two together. In addition, both fragments must deal with situations where the other Fragment has not yet been created or is not visible.
You can use ViewModel objects to solve this common difficulty. The two fragments can handle such communication using their Activity scope shared ViewModel, as shown in the following sample code:
Notice that both fragments retrieve the activities that contain them. This way, when both fragments get their own ViewModelProvider, they receive the same SharedViewModel instance (whose scope is limited to that Activity).
This approach has the following advantages:
- The Activity does not need to perform any action, nor does it need to know anything about this communication.
- In addition to
SharedViewModel
Fragments do not need to know each other outside of convention. If one Fragment disappears, the other Fragment continues to work as usual. - Each Fragment has its own life cycle and is not affected by the life cycle of the other Fragment. If one Fragment replaces another, the interface will continue to work without any problems.
Replace the loader with the ViewModel
Loader classes such as CursorLoader are often used to keep data in the application interface in sync with the database. You can use the ViewModel with some other classes to replace the loader. Using the ViewModel separates the interface controller from the data loading operation, which means fewer strong references between classes.
In a common way of using a loader, an application might use the CursorLoader to view the contents of a database. When a value in the database changes, the loader automatically triggers a data reload and updates the interface:
Figure 2. Loading data using the loader
The ViewModel uses an alternative loader with Room and LiveData. The ViewModel ensures that the data survives device configuration changes. Room notifies LiveData when the database changes, and LiveData updates the interface with the revised data.
Figure 3. Loading data using ViewModel
Use coroutines with viewModels
The ViewModel supports Kotlin coroutines. For more details, see Using Kotlin coroutines with Android architecture components. (Official Google docs)