Using Android LiveData
By: Kayvan Kaseb
Tags: Android App Development, Android, Android Apps, Livedata

photo provided by https://unsplash.com/photos/w33-zg-dNL4

As an Android Developer, you can use ViewModel concept in your architecture to communicate among various views of your app. However, the situation could be more complicated due to a large number of data as well as rotation of your screen. As a result, Google introduced the concept of LiveData in Android development for addressing these issues. In fact, LiveData is an observable data holder class, which is also lifecycle aware. This essay aims to discuss the concept of LiveData, and implement it with ViewModel and Data Binding as well.

Overview and Introduction

LiveData is an observable data holder class, which is also lifecycle aware. For instance, if you have your UI as well as LiveData object that holds some data, which you want to show on screen. In this case, the UI observes the LiveData object. This means the UI wants to be notified for new updates. As a result, when the LiveData changes, the UI will get notified, and then the UI can be able to redraw itself with the new data. In other words, LiveData makes it easy to keep what is going on screen in sync with the data. However, LiveData checks the state of the observer for the first step before any updates.

LiveData is an observable data holder class. Unlike a regular observable, LiveData is lifecycle-aware, meaning it respects the lifecycle of other app components, such as activities, fragments, or services. This awareness ensures LiveData only updates app component observers that are in an active lifecycle state.

What makes LiveData different from other observable approaches is that it is also lifecycle aware. It means that it understands whether your UI is on screen, off-screen or destroyed. LiveData knows about your UI state because you passed it when you call Observe.

Implementing the LiveData

To implement LiveData in your Android projects, you should follow some steps as follows:

Creating a LiveData object in ViewModel class

In fact, LiveData objects will usually be kept in the ViewModel class, and be accessed via a getter method. So, the first step is crating the LiveData object in ViewModel class. In addition, LiveData is a wrapper that can be used with any types of data like list.

class SampleViewModel : ViewModel() {



// Create a LiveData with a String

val currentName: MutableLiveData<String> by lazy {

MutableLiveData<String>()

}



// Rest of the ViewModel...

}

For example, if you create an activity and ViewModel for a user profile, you will have this user LiveData object that holds a User object.

class UserViewModel: ViewModel() { private val _user MutableLiveData<User>()

val user : LiveData<User>

get() = _user }

Observing the LiveData object

Now, over in your activity’s onCreate method, you will get that LiveData from the ViewModel class. It means you call observe on the LiveData.

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState) // Get the ViewModel

viewModel=ViewModelProviders.of(this).get(UserViewModel::class.java // Rest of the onCreate method...



} override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState) // Rest of the onCreate method...

// Creating the observer for updating the UI

val sampleObserver = Observer<String> { newName -> // Update the UI, in this example, a TextView.

sampleTextView.text = newName

} // Observe the LiveData

viewModel.currentName.observe(this, nameObserver)

}

To use LiveData in our Activity along with the ViewModel wholly, after registering the Observer in the Activity, we must override the onChanged() method, and you will define the operation that will be triggered when there is a change in data.

Therefore, to get LiveData object form ViewModel as above for User object, you can do it in onCreate method as follows:

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)



userViewModel.user.observe(this, Observer { user -> userNameTextView.text = user?.name



})

}

The first argument is “this”. That means you must pass the UI, which is activity in this case. Besides, the second argument is an Observer, which is just a callback. However, if you use Data Binding library, you will not need to manually set up these observers. Thus, you can remove all the above boilerplate because the TextView’s XML references the LiveData directly in activity_main.xml as below:

<TextView

...

android:text = "@{viewmodel.user.name}" />

Also, you should set the variable tag in layout for your viewmodel:

<layout>

<data>

<variable>

name="viewmodel"

type="android.example.com.UserViewModel"/>

</data>

<.../>

</layout>

As a matter of fact, if you are using Android studio 3.1 or higher, LiveData and ViewModel work with Data Binding.

ViewModel + LiveData + Data Binding = Reactive UI

Basically, after associating your ViewModel and Data Binding layout in onCreate() method via binding and inflate method, you just need to add this single line change layout. In other words, you must add this single line change to have your LiveData be properly observed when bound to the XML.

binding.setLifecycleOwner(this)

In short, this is the magic that lets the binding update whenever the LiveData updates, and the ViewModel’s onscreen. As a result, if your activity is not on screen, your LiveData does not trigger useless UI updates. This means if the activity or UI gets destroyed, the observation connection is cleaned up automatically by LiveData. Thus, you will never accidentally trigger an activity or fragment that is off-screen or destroyed to redraw itself. This is possible because of interfaces and classes in the Lifecycle library that are used by framework classes as well. These classes are as follows: Lifecycle, LifecycleOwner, and LifecycleObserver.

Lifecycle: is an object that defines the Android Lifecycle and what state it is in.

2. LifecycleOwner: is an interface for objects, which have a lifecycle like AppCompatActvity or Activity or Fragment

3. LifecycleObserver: is an interface for observing a lifecycle.

Updating LiveData Object

In this step we should update the object of the LiveData. For this purpose, you can either use the setValue(T) method or the postValue(T) method of the MutableLiveData class. The reason for using the MutableLiveData class only is that the LiveData has no public methods for updating the stored value. Therefore, we must use MutableLiveData class. For instance, it triggers all observers when we press a button. So, the onChanged() method will be called when we press the button, and the value for this method will contain Android LiveData.

button.setOnClickListener { val sampleName = "Android LiveData"

model.currentName.setValue(sampleName) }

Use setValue if you are running on the UI thread, and then use postValue if you are running on a background thread. As a result, when either setValue or postValue is called, the LiveData notifies active observers.

Extending LiveData

As you know, you can use LiveData only and update changes when the observer is in active state. LiveData considers an observer to be in an active state if the observer’s lifecycle is in either the STARTED or RESUMED states. Thus, you can extend the LiveData class in your observer class and override the onActive() and onInactive() method. This onActive() method will be used to check if there is a change in LiveData or not. Also, the onInactive() method is used to remove the updates.

Transforming LiveData

Initially, you may want to make changes to the value stored in a LiveData object before dispatching it to the observers, or you may need to return a different LiveData instance. The Lifecycle package supports the Transformation class that contains helper methods, which provide these approaches.

map(): lets you apply a function to the output of LiveData and then propagates the result to the downstream. For instance, in a scenario where we need to show Snackbar whenever a new user added to the database. The Snackbar shows data of a custom String with username added in it.

switchMap(): this function transformation is similar to map, but for mapping functions that emit LiveData instead of values that is used. For instance, if you have a bunch of users, probably stored in a Room database, you might have a lookup function for those users. By using switchMap(), you will have a LiveData for the user ID. Whenever the ID changes, your user lookup function would be called with that ID. In other words, no matter how many different times you call this look up function and get a different LiveData, your UI just only needs to observe the result LiveData once, which is the power of switchMap. The switchMap functions propagate LiveData to downstream instead of single value. Another example where you need to search user by username and show the result inside the RecyclerView.

MediatorLiveData(): if you want to make your own custom data transformations, you should consider the MediatorLiveData class. MediatorLiveData includes methods to add and remove source LiveData objects. So, you can combine and propagate events form all these sources downstream.

Some benefits of LiveData

An appropriate update in UI: With the help of LiveData, your application’s UI will only be changed if there is a change in the data. No crash because of stopped activities: Only that observer will be notified that is in a live state. Thus, an app crash will not be occurred because of stopped or paused activities. Proper configuration changes: If an activity or fragment is recreated because of a configuration change like device rotation, it immediately receives the latest state. Up-to-date data: The observer will have the latest data even if the observer is in background or pause state. Whenever the observer is resumed then the latest updated data will be provided to the observer. No memory leaks: Because all the observers are bound to Lifecycle objects and are cleaned up when their associated lifecycle is destroyed, there are no memory leaks.

In conclusion

In fact, LiveData is used to notify the user if there is a change in the value of objects of LiveData. In this essay, we considered the concept of LiveData, and implement it with ViewModel and Data Binding as well. Additionally, we noticed that this concept has some positive consequences for managing data on View in some special situations like rotations of screen. which plays a key role in maintaining and updating the states of objects.