Writing a Transformation
Another RxJava transformation operator is filter()
. This takes a stream of objects and a function that tests each object and returns true
for the ones to be sent downstream. The ones that test out to false
are dropped. Hence, the stream becomes filtered by whatever rule is encoded in that function.
Transformations
does not have a filter()
method, but we can write one, to see what a transformation method looks like.
Earlier in the book, we had the Sensor/LiveList
sample, where we had a LiveData
reporting sensor events, specifically the light level. The Sensor/LiveFilter
sample project is a clone of that project, one that introduces a filter, to only report those readings that fall between 20 and 40 lux.
To that end, we have a LiveTransmogrifiers
class that serves as a home for our transformation methods:
package com.commonsware.android.livedata;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MediatorLiveData;
import android.support.annotation.MainThread;
import android.support.annotation.NonNull;
class LiveTransmogrifiers {
interface Confirmer<T> {
boolean test(T thingy);
}
@MainThread
static <X> LiveData<X> filter(@NonNull LiveData<X> source,
@NonNull final Confirmer<X> confirmer) {
final MediatorLiveData<X> result=new MediatorLiveData<>();
result.addSource(source, x -> {
if (confirmer.test(x)) {
result.setValue(x);
}
});
return(result);
}
}
The RxJava filter()
operator uses a Predicate
as the function for testing an object to determine if it should be passed or not. Unfortunately, Predicate
is part of the Java 8 classes added in Android 7.0, and so it is unavailable for older devices. So, we have a Confirmer
interface that fills that role. The test()
method on a Confirmer
needs to return true
for objects that should pass the filter, false
otherwise.
The filter()
method on LiveTransmogrifiers
takes a LiveData
of some type and a Confirmer
of that type. It then uses a MediatorLiveData
, which is a LiveData
object that can chain onto an existing LiveData
and expose the onChanged()
method for outside parties to use. In this case, our lambda uses the Confirmer
to see if the new object passes the test()
, and if it does, we call setValue()
on the MediatorLiveData
to have that object flow along to anything that observes that MediatorLiveData
. filter()
then returns that MediatorLiveData
. The net effect is as if filter()
wraps the original LiveData
in another LiveData
that applies our filtering rule.
We can now use filter()
to limit the readings that we get from the sensor:
final LiveData<SensorLiveData.Event> filtered=
LiveTransmogrifiers.filter(state.sensorLiveData,
event -> (event.values[0]>20 && event.values[0]<40));
filtered.observe(this, event -> adapter.add(event));
We pass our original SensorLiveData
to filter()
, along with a Confirmer
(in the form of a lambda expression) that sees if the light level is between 20 and 40. Then, we observe the results of the filter()
call and only add those objects — not every reading from the SensorLiveData
— to the EventLogAdapter
.
The net result, if you compare and contrast the output of this sample with the original, is that while the original reports everything, this new sample only reports a subset of the data.
Prev Table of Contents Next
This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.