LiveData library allows developers to update the UI seamlessly as data updates regardless of the data type. This is a great addition to DRY Principle as well. Moreover, It works perfectly well with the ViewModel library which makes this a great choice for the developers.
In case you are unaware of the ViewModel library, feel free to check out my old article on it.
Usage
Dependency
To use LiveData, we need to add a dependency on the androidx-lifecycle
library.
dependencies {
// LiveData
def lifecycle_version = "2.2.0"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
}
Implementations
LiveData has 2 implementations:
- MutableLiveData that is mutable, and
- LiveData that is immutable.
It looks like MutableLiveData
differs from LiveData
only by making the setValue()
and postValue()
methods public, whereas in LiveData
they are protected.
What are some reasons to make a separate class for this change and not simply define those methods as public in the LiveData
itself?
Generally speaking, is such…
Using MutableLiveData
We will be creating a simple counter app to show the usage of the library. You should be able to use it in a similar pattern with the ViewModel library and any of your other projects.
Here is how my activity_main.xml
looks like:
<?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=".MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:textSize="40sp"
app:layout_constraintBottom_toTopOf="@+id/button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
</androidx.constraintlayout.widget.ConstraintLayout>
Now we will create a variable to store the counter value and increment it on button press. This variable will be of type MutableLiveData<Int>
and will create an instance of the MutableLiveData
class with a constructor of the initial value.
var counter: MutableLiveData<Int> = MutableLiveData(0)
Value of LiveData object can be obtained by using value property
.
Now we can call the observe
method over it to do some specific action every time the value updates. This requires arguments for the class owner and Observer
function in which we can define our actions.
I will be using ViewBinding to interact with GUI components. In case you are unaware of the topic, you can check out my article over it.
counter.observe(this, Observer {
binding.textView.text = counter.value.toString()
})
and we need to implement the logic in the button to actually increment the counter.
binding.button.setOnClickListener {
counter.value?.let {
counter.value = it + 1
}
}
Here is how MainActivity.kt
looks in my case:
package dev.theimpulson.livedataexample
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import dev.theimpulson.livedataexample.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val counter: MutableLiveData<Int> = MutableLiveData(0)
counter.observe(this, Observer {
binding.textView.text = counter.value.toString()
})
binding.button.setOnClickListener {
counter.value?.let {
counter.value = it + 1
}
}
}
}
With this our app is now complete. You can build and run it to observe that UI will increment as soon as you click the button.
Using LiveData
As I stated earlier that LiveData is immutable. Therefore, one cannot set its value. This is particularly useful if you want to make an object observable but with a private setter.
In the current case, here is how it could have been done:
private val _counter: MutableLiveData<Int> = MutableLiveData(0)
fun counter(): LiveData<Int> {
return _counter
}
counter().observe(this, Observer {
binding.textView.text = counter().value.toString()
})
binding.button.setOnClickListener {
_counter.value?.let {
_counter.value = it + 1
}
}
As you can notice that I made _counter
which is of type MutableLiveData
which is private and it will be limited to the class. In case one wants to observe its value, they must use the counter()
function which is of type LiveData
whose setter is private. This will ensure that one cannot modify the value of the counter outside of the class. I hope that clears how you can use LiveData as well.
While these were very short example, I hope this still makes it clear how powerful and useful this library is for the developers. I recommend using LiveData in conjunction with ViewModel to get the most out of both libraries.
Top comments (0)