Learn how to use LiveData
Posted on April 2, 2018 • 6 minutes • 1122 words
Table of contents
In this post i want to explain what is LiveData and how you can use it.
1) What is LiveData
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. -GoogleAs quoted above, LiveData is a data holder class, introduced as part of the Android Architecture Components at Google I/O '17. I see it as a lite version of RxJava's Observables with the advantage that it only emits data when the observer is active. With using LiveData your UI can always react to the newest values of the data you are observing.
Observe changes
Asynchronous
To observe the changes of a LiveData asynchronous you have two options:Observe with LifeCycle Owner
count1.observe(this, new Observer() {
@Override public void onChanged(@Nullable Integer i) {
//Do something with "integer"
}
});
observe(LifeCycleOwner,Observer)
The observe() method needs a LifeCyclerOwner and an Observer.
A LifeCycleOwner is a class which implements a LifeCycleOwner interface like Activity or Fragment. The advantage is that LiveData detects the LifeCycle and it will be automatic removed when your class is destroyed.
The onChanged() method will be triggered every time something changes the value of the observed LiveData.
Observe without LifeCycle Owner
count1.observeForever(new Observer() {
@Override public void onChanged(@Nullable Integer integer) {
//Do something with "integer"
}
});
observeForever(Observer)
You can also use observeForever() to observe LiveData without a LifeCycleOwner, but as the name indicates it will not be removed automatically. It is important to manually remove the observer with removeObserver(), otherwise it could cause memory leaks.
Synchronous
count1.getValue()
getValue()
getValue() will return the value of LiveData at the moment the method gets called. You will not be informed about any changes at the object.
2) MutableLiveData
MutableLiveData is a Subclass of LiveData. Because LiveData has no methods to change the value, you have to use a MutableLiveData. It offers two methods to set a value.MutableLiveData count1 = new MutableLiveData<>();
setValue()
You have to use setValue() when you are working on the mainThread.
count1.setValue(count);
postValue()
You have to use postValue(), when you are changing the value from a background thread.
//Set the value of mCurrentCount on backgroundThread
count1.postValue(count);
3) Transformations of LiveData
LiveData comes with a few special classes that are useful if you need to transform your observed LiveData Objects.3.1) Transformations
.map()public LiveData<String> getCount1AsString() {
return Transformations.map(getCount(), input -> String.valueOf(input));
}
Transformations can be useful when you have data stored as LiveData, but you might have a special case where you want to make changes before to the emitting the data. You can see an example below in Scenario 2 .
.switchMap()
switchMap() does the same as map(), but it has to return a LiveData object.
3.2) MediatorLiveData
MediatorLiveData is a LiveData class which can observe multiple LiveData Objects.private MediatorLiveData mediatorLiveData = new MediatorLiveData<>();
public MediatorLiveData<Integer> getMediatorLiveData() {
mediatorLiveData.addSource(getCount(), new Observer() {
@Override public void onChanged(@Nullable Integer integer) {
//Do something with "integer"
}
});
mediatorLiveData.addSource(getCount2(), new Observer() {
@Override public void onChanged(@Nullable Integer integer) {
//Do something with "integer"
}
});
return mediatorLiveData;
}
addSource(LiveData, Observer)
Starts to listen the given source LiveData, onChanged observer will be called when source value was changed.
removeSource(LiveData)
Stops to listen the given LiveData.
setValue(Object)
You can set the value like a MutableLiveData object.
You can see an example in Scenario 4 .
3.3) LiveDataReactiveStreams
With LiveDataReactiveStreams you can transform LiveData to RxJava and vice versa. It has two methods.LiveDataReactiveStreams.toPublisher()
This method will observe a LiveData object and converts it to an Observable.
LiveDataReactiveStreams.fromPublisher()
You can use this method convert Flowables from RxJava into LiveData.
You can see an example in Scenario 5 and Scenario 6 .
4) Add to project
Add this to your build.gradle // ViewModel and LiveData
implementation "android.arch.lifecycle:extensions:${architectureVersion}"
annotationProcessor "android.arch.lifecycle:compiler:${architectureVersion}"
If you want to use LiveDataReactiveStreams to combine LiveData with RxJava have to also add this:
//LiveData Rx
compile "android.arch.lifecycle:reactivestreams:${architectureVersion}"
5) Example project
I created an example project that is using LiveData in different scenarios, you can find it on Github.Scenario 1
In the ViewModel i created a variable “count” that holds a value and a MutableLiveData that will emit the object “count”.
private static int count = 0;
private static MutableLiveData count1;
The Buttons +1 and -1 tell the ViewModel to increase or decrease the value of count.
public void incrementCount1() {
count++;
count1.setValue(count);
}
The value of count gets increased, then it will be set as the value of the MutableLiveData count1
public LiveData<Integer> getCount() {
if (count1 == null) {
count1 = new MutableLiveDat<>();
}
return count1;
}
The ViewModel has a getCount() method to get the MutableLiveData. It is returned as LiveData so the other classes than the ViewModel have no option to change the value of it and can just observe the value.
mModel.getCount()
.observe(this, s ->
count1IntegerTv.setText("count1 as Integer: " + String.valueOf(s))
);
In the MainActivity i am using the method to observe count1 and set the value to a TextView.
Scenario 2
public LiveData<String> getCount1AsString() {
return Transformations.map(getCount(), input -> String.valueOf(input));
}
Here i’m using the Transformations class to emit the value of count as a String. The map method observes the value of getCount() and converts the value to a String. Then it emits this String as LiveData.
mModel.getCount1AsString().observe(this, s -> countStringTv.setText("count1 as String: " + s));
Like in Scenario 1 i’m observing the changes of getCount1AsString() and set the value to a TextView.
Scenario 3
This does the same as Scenario 1 but it has the variable secoundCount instead of count. It's just there to show the use of MediatorLiveData in Scenario 4.Scenario 4
private MediatorLiveData<Integer> mediatorLiveData = new MediatorLiveData<>();
public MediatorLiveData<Integer> getMediatorLiveData() {
mediatorLiveData.addSource(getCount(), integer -> mediatorLiveData.setValue(integer));
mediatorLiveData.addSource(getCount2(), integer -> mediatorLiveData.setValue(integer));
return mediatorLiveData;
}
Here i’m using MediatorLiveData. I’m adding getCount() and getCount2() as a source for it. Every time one of the two sources change, the value will be set as a value of MediatorLiveData.
Scenario 5
Here i'm using LiveDataReactiveStreams.fromPublisher to show how you can convert a Flowable from RxJava to a LiveData Object.public Flowable<String> testFlowable() {
return Observable.just("Hello World")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.toFlowable(BackpressureStrategy.LATEST);
}
public LiveData<String> fromFlowable() {
return LiveDataReactiveStreams.fromPublisher(testFlowable());
}
My test Flowable will only emit the String “Hello World” one time.
Scenario 6
LiveDataReactiveStreams.toPublisher(this, mModel.getCount())
.subscribe(new DisposableSubscriber<Integer>() {
@Override public void onNext(Integer integer) {
rxJava.setText("LiveData to RxJava: " + String.valueOf(integer));
}
@Override public void onError(Throwable t) {
Log.i("MainActivity", "onError: ");
}
@Override public void onComplete() {
Log.i("MainActivity", "onNext: ");
}
});
Here i’m using LiveDataReactiveStreams.toPublisher. It is the opposite of Scenario 5. A LiveData objects gets converted to a Observable.