Working with LiveData and ViewModel

Harjot Singh
HealthifyMe Tech
Published in
5 min readFeb 11, 2022
Owl
Photo by Richard Lee on Unsplash

Introduction:

LiveData is an Observable data class. Unlike RxJava’s Observable it is lifecycle-aware which means it notifies Observer objects only when the app components like Activity, Fragment or Service is in active state.

Active state is when the app component’s lifecycle is in STARTED or RESUMED state.

In the diagram above, LiveData could be modified from ViewModel and here Observers are the app components that have Android lifecycle i.e. activity or fragment. Since Observer{1,4,5} are in active state(STARTED or RESUMED), LiveData notifies these observer objects while the Observers{2,3} with the inactive state(PAUSED or DESTROYED) won’t be getting notified about the updates from LiveData object.

Why to use LiveData?

a. Lifecycle-aware : Because LiveData is lifecycle aware, let me re-iterate, it will notify those observer objects which are in active state.

b. The UI can be updated more easily. Whenever the data change happens, it will notify the observers about the changes.

c. The Observer objects clean up themselves when the associated lifecycle is destroyed. (In RxJava, we have to handle unsubscribing of Observer in PAUSED or DESTROYED state)

MutableLiveData and LiveData

a. MutableLiveData, as the name suggests, is mutable i.e. it’s value can be changed whereas, LiveData is read-only.

b. LiveData is an abstract class and MutableLiveData is a child of it.

c. LiveData is always initialized with one of it’s child class’s constructors except Transformation and MediatorLiveData.

Implementation

Let’s quickly go through the sample project that I’m working on:

In this project, we have an activity with one TextView and two Buttons.
Change Text Button will change the text of the TextView and Undo Text Button replaces with the text with “Hello World!”. I have used ViewBinding for this project. The blog for ViewBinding will be written shortly, so stay tuned for that. :)

Now enough talking, let’s jump into the good stuff:

Step 1: Initializing LiveData in our ViewModel

val mLiveData = MutableLiveData<String>()

a. mLiveData is declared as val, so it cannot change but it’s value can always be updated. Now, any change in its value would notify the observers about it.

But wait….doesn’t this mean that mLiveData can be changed from any class inside the project….. now enters accessibility modifier into the picture: we’ll make it private.

Okay…good solution, but then how can we access the LiveData then?

The answer is simple. Remember, LiveData is read only?
We’ll take that into the picture. So the code becomes:

private val mLiveData = MutableLiveData<String>()fun getLiveData(): LiveData<String> = mLiveData

b. The type of MutableLiveData here is String because we want to observe the text that would be changing in the activity.

Step 2: Initialize viewModel inside our MainActivity class

private val viewModel = ViewModelProvider(this)
.get(MyViewModel::class.java)

ViewModelProvider is a class that creates ViewModels and retain them in a store of given ViewModelStoreOwner. Here, we’ve passed as this as the owner.

Step 3: Observe the live data

//textView is the Text with "Hello World!" 
viewModel.getLiveData().observe(this) { text ->
textView.text = text
}
OR
//this function can also be written as
viewModel.getLiveData().observe(this,Observer{ text ->
textView.text = text
})
//both do the same work...so don't you get confused

observe() takes LifecycleOwner and Observer as its argument.

(Note: LifecycleOwner in simple words is a class that has Android lifecycle)

Alright, now we have created LiveData, intialized ViewModel, started observing the data. But, how the LiveData’s value will keep on changing?

In real scenarios, the data gets changed by backend and then liveData is needed, but in our example, we’ll kinda do it in a hacky way.
Let’s change the text when “Change text” button is clicked. Consider it as a data change from API; when the value of LiveData changes, the observer would notify about it in the MainActivity.

So, we’ll create a new method changeText() inside our ViewModel class which would be called whenever “Change text” button is clicked.

Inside MainActivity class

btnChangeText.setOnClickListener {
viewModel.changeText()
}

Inside ViewModel class

fun changeText(){
mLiveData.value = "The text has been changed"
}

And there it is…phew!
Finally done!

Let’s see the results now:

To save you time, I have copy pasted the code here and it’ll be available on my Github link too. So either copy from here and paste into your project or clone the project from Github. Cheers!

CODE:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".presentation.MainActivity">

<TextView
android:id="@+id/tv_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<Button
android:id="@+id/btn_change_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
app:layout_constraintTop_toBottomOf="@id/tv_text"
app:layout_constraintStart_toStartOf="@id/tv_text"
app:layout_constraintEnd_toEndOf="@id/tv_text"
android:textAllCaps="false"
android:text="Change Text"/>

<Button
android:id="@+id/btn_undo_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
app:layout_constraintTop_toBottomOf="@id/btn_change_text"
app:layout_constraintStart_toStartOf="@id/tv_text"
app:layout_constraintEnd_toEndOf="@id/tv_text"
android:textAllCaps="false"
android:text="Undo Text"/>

</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.kt

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
import com.example.livedatapractice.databinding.ActivityMainBinding
import com.example.livedatapractice.viewmodel.MyViewModel
class MainActivity : AppCompatActivity() {
private val binding by lazy {
ActivityMainBinding.inflate(layoutInflater)
}
//initializing viewModel lazily too
private val viewModel: MyViewModel by lazy {
ViewModelProvider(this).get(MyViewModel::class.java)
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
val btnChangeText = binding.btnChangeText
val btnUndoText = binding.btnUndoText
val textView = binding.tvText

//Observing the live data
viewModel.getLiveData().observe(this) { text ->
textView.text = text
}

btnChangeText.setOnClickListener {
viewModel.changeText()
}
btnUndoText.setOnClickListener {
textView.text = "Hello World!"
}
}
}

MyViewModel.kt

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
class MyViewModel: ViewModel() { private val mLiveData = MutableLiveData<String>()

/* Now live data is read only, henceforth we need not worry about the value being changed
through some other class's code. */
fun getLiveData(): LiveData<String> = mLiveData

fun changeText(){
mLiveData.value = "The text has been changed"
}
}

Thank you for reading :)
Happy coding!

By: Harjot Singh

--

--

Harjot Singh
HealthifyMe Tech

Android Developer @HealhifyMe I lose my handwritten notes often...so now making sure I can find my notes easily :p