ViewModel in Android


Activity Lifecycle in brief:
The activity class goes through various states, as user navigates through, out of and back to our app. Activity class provides many callbacks that allow the activity to know that the state has changed: that the system is creating, stopping or resuming an activity or destroying the process in which the activity resides. Good implementation of lifecycle callbacks makes sure that the app avoids crashes, frees up the valuable system resource, preserves users progress and maintains data consistency in configuration change.

Fig: Activity lifecycle

Lifecycle-aware components perform actions in response to a change in the lifecycle status of an activity or fragment. They help us to write better-organized, maintainable and light-weight codes. The lifecycles are managed by the operating system that runs our process.

Lifecycle defines an object that has an Android Lifecycle. LiveData is a data holder class that can be observed within a given lifecycle. This means that an Observer can be added in a pair with a LifecycleOwner, and this observer will be notified about modifications of the wrapped data only if the paired LifecycleOwner is in active state.

Lifecycle owner is considered as active, it its state is Lifecycle.Stare.STARTED or Lifecycle.State.RESUMED.

An observer added with a Lifecycle will be automatically removed if the corresponding Lifecycle moves to Lifecycle.State.Destroyed state.

Observer pattern is used when there is one-to-many relationship between objects such as if one object is modified, its dependent objects are to be notified automatically.

Best practices for lifecycle-aware components:

  • Keep UI controllers lean – ViewModels must be used to acquire data, observe LiveData object to reflects changes back to the views.
  • Write data-driven UIs – UI controllers – updates views as data changes – notify user actions back to ViewModel.
  • Data logic needs to be in ViewModel class – viewmodel provides connecdtion to the UI with the rest of the app.
  • Use Data Binding.
  • Avoid referencing a view or activity context in ViewModel
  • Use Kotlin coroutines to manage long running tasks and other asynchronous tasks.

ViewModels:

The ViewModel separates the view with the model. Instead of making the model aware of the user’s view of a data, so that it converts the data to the display format, the model simply holds the data, the view simply holds the formatted data and the viewmodel acts as a liaison between the two. The main business logic in the application is carried out in this module. Data is received from Model and processed accordingly and emits/exposes data which is observed by View.

Fig: Model-View-ViewModel concept

The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way. The ViewModel class allows to survive configuration such as screen rotations.

To maintain data consistency throughout the configuration change, viewmodels are used. UI controllers such as activities and fragments are primarily intended to display UI data, react to user actions or handle the permission request. Requiring UI controllers to also be responsible for loading data from a database or network add bloat to the class.

Architecture Components provides a ViewModel helper class for the UI controller that is responsible for preparing data for the UI. ViewModel objects are automatically retained during configuration changes so that data they hold is immediately available to the next activity of fragment instance. For example, if you need to display a list of users in our app, we need to make sure that we assign responsibilty to acquire and keep the list of users to a ViewModel, instead of an Activity or fragment.

ViewModel objects – outlive specific instantations of views or LifecycleOwners

Fig: Lifecycle of Viewmodel

ViewModel objects can contain LifecycleObservers – such as LiveData objects. However, ViewModel objects must never observe changes to lifecycle-aware observables, such as LiveData objects.
If the ViewModel needs the Application context, for example, to find a system service, it can extend the AndroidViewModel class and have a constructor that receives the Application in the constructor, since Application class extends Context.

Share data between fragments:

One activity can have several fragments which are related with one-another. The user input passed in one fragment, can be provided as an information in another fragment. It’s very common that two or more fragments in an activity need to communicate with each other. For example in a list-detail fragment, if user selects an item from a list, and shows the detail in another fragment, there needs to be some interface description to pass the data between these two. The owner activity must bind these two together.
This scenario can be addressed by using ViewModel objects. These fragments can share a ViewModel using their activity scope to handle this communication, as illustrated by the following sample code:

Create a ViewModel class:

To pass data from Input fragment, just pass the data to ViewModel object like this:
Post value like this:
Get the value in another fragment, like this:
We should only keep in mind that we need to create the ViewModel instance in the activity scope, otherwise android will create a separate instance rather than sharing the same instance and we will not get the data.

This approach offers the following benefits:

  • Activity does not need to do or know anything about the communication.
  • Fragments dont need to know about each other besides the SharedViewModel contract.
  • If one fragment replaces the other one, the UI continues to work without any problems.

Replacing Loaders with ViewModel

ViewModel works with Room and LiveData to replace the loader. The ViewModel ensures that the data survives a device configuration change. Room informs your LiveData when the database changes, and the LiveData, in turn, updates your UI with the revised data.

Use coroutines with ViewModel

ViewModel includes support for Kotlin coroutines. Kotlin coroutines provide an API that enables us to write asynchronous code. With Kotlin coroutines, we can define a CoroutineScope, which helps us to manage when our coroutines should run.

Lifecycle-aware corountine scopes

ViewModelScope:
A ViewModelScope is defined for each ViewModel in our app. Any coroutines launched in this scope is automatically canceled if the ViewModel is cleared. Coroutines are useful when we have work that needs to be done only if the ViewModel is active.

The purpose of the ViewModel is to encapsulate the data for a UI controller to let the data survive configuration changes.

Leave a comment