Rxjava For Android App Development PDF
Rxjava For Android App Development PDF
Android App
Development
A Quick Look for Developers
K. Matt Dupree
Additional
Resources
4 Easy Ways to Learn More and Stay Current
Programming Newsletter
Get programming r elated news and content delivered weekly to your inbox.
oreilly.com/programming/newsletter
OReilly Radar
Read more insight and analysis about emerging technologies.
radar.oreilly.com
Conferences
Immerse yourself in learning at an upcoming OReilly conference.
conferences.oreilly.com
2015 OReilly Media, Inc. The OReilly logo is a registered trademark of OReilly Media, Inc. #15305
K. Matthew Dupree
First Edition
978-1-491-93933-8
[LSI]
Table of Contents
An Introduction to RxJava. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Sharp Learning Curve, Big Rewards
Observables
Observers
Observable Creation and Subscribers
Schedulers
Operators
Conclusion
1
3
4
6
8
10
13
15
21
29
31
32
iii
An Introduction to RxJava
Lets start with the guiding example that will help us get a handle on
RxJava. Imagine we are building a HackerNews client, an app that
allows users to read HackerNews stories and comments. Our Hack
erNews client might look a little like Figure 1-1:
An Introduction to RxJava
Observables
RxJavas asynchronous data streams are emitted by Observa
bles. The reactive extensions website calls Observables the asyn
chronous/push dual' to the synchronous/pull Iterable.
Although Javas Iterable is not a perfect dual of RxJavas Observa
bles, reminding ourselves how Javas Iterables work can be a help
ful way of introducing Observables and asynchronous data streams.
Every time we use the for-each syntax to iterate over a Collection,
we are taking advantage of Iterables. If we were building our
HackerNews client, we might loop over a list of Storys and log the
titles of those Storys:
for (Story story : stories) {
Log.i(TAG, story.getTitle());
}
stories.iterator();
itera
ment for using for-each syntax while writing Android apps. Google explicitly warns us
that there are cases where this is inappropriate.
Observables
nous ones.
To make this distinction more concrete, think again about the pre
ceding snippet that logs a HackerNews storys title within a Collec
tion<Story>. Now imagine that the Storys logged in that snippet
were not available in memory, that each story had to be fetched
from the network, and that we wanted to log the Storys on the main
thread. In this case, we would need the stream of Storys to be an
asynchronous stream and using an Iterable to access each element
in that stream would be inappropriate.
Instead, in this case, we should use an Observable to access each
story as it is returned by the HackerNews API. Now, we know that
we can access an element in an Iterables stream of data by calling
Iterator.next() on its Iterator. We do not know, however, how
to access the elements of an Observables asynchronous data stream.
This brings us to the second fundamental concept in RxJava: the
Observer.
Observers
Observers are consumers of an Observables asynchronous data
stream. Observers can react to the data emitted by the Observable
in whatever way they want. For example, here is an Observer that
logs the titles of Storys emitted by an Observable:
storiesObservable.subscribe(new Observer<Story>() {
@Override
public void onCompleted() {}
@Override
public void onNext(Story story) {
An Introduction to RxJava
Log.i(TAG, story.getTitle());
}
//...
});
Note that this code is very similar to the previous for-each snippet.
In both snippets, we are consuming a data stream with a welldefined termination point. When we loop through a Collection
using the for-each syntax, the loop terminates when iterator.has
Next() returns false. Similarly, in the preceding code, the Observer
knows that there are no more elements left in the asynchronous data
stream when onCompleted() is called.
The main difference between these two snippets is that when we
loop over a Collection, were logging the Story titles synchro
nously and we when subscribe to the stringsObservable, were reg
istering to log the Story titles asynchronously as they become avail
able.
An Observer can also handle any exceptions that may occur while
the Observable is emitting its data. Observers handle these errors in
their onError() method.
To see why this is a useful feature of RxJava, imagine for a moment
that the Story objects emitted by the Observable are objects that are
converted from a JSON response to a HackerNews API call. If the
HackerNews API returned malformed JSON, which in turn caused
an exception in converting the JSON to Story model objects, the
Observer would receive a call to onError(), with the exception that
was thrown when the malformed JSON was being parsed.
At this point, there are two pieces of the aforementioned definition
of RxJava that should be clearer. To see this, lets take a second look
at that definition:
RxJava is a library that allows us to represent any operation as an
asynchronous data stream that can be created on any thread, declara
tively composed, and consumed by multiple objects on any thread.
We have just seen that Observables are what allow us to represent
any operation as an asynchronous data stream. Observables are simi
lar to Iterables in that they both provide access to data streams
with well-defined termination points. We also now know an impor
tant difference between Observables and Iterables: Observables
Observers
There are still some things from the preceding definition of RxJava
that are unclear. How exactly does RxJava allow us to represent any
operation as an asynchronous data stream? In other words, how do
Observables emit the items that make up their asynchronous data
streams? Where do those items come from? These are questions that
we will address in the next section.
whose main aim is well captured by the Gang of Four in Design Pat
terns: Elements of Reusable Object-Oriented Software:
Provide a way to access the elements of an aggregate object without
exposing its underlying representation.4
The Iterator pattern allows any object to provide access to its ele
ments without exposing that objects underlying representation.
Similarly, Observables provide access to the elements of an asyn
chronous data stream in a way that completely hides and is largely
independent of the process by which that data stream is created.
This allows Observables to represent virtually any operation.
Here is an example that will make the Observables flexibility more
concrete. Observables are typically created by passing in a function
object that fetches the items of an asynchronous data stream and
notifies a Subscriber that those items have become available. A
An Introduction to RxJava
Here is how you would create an Observable that emits some Hack
erNews Storys that have been fetched from the API:
Observable.create(new Observable.OnSubscribe<Story>() { //1
@Override
public void call(Subscriber<? super Story> subscriber) {
if (!subscriber.isUnsubscribed()) { //2
try {
Story topStory = hackerNewsRestAdapter.getTop
Story(); //3
subscriber.onNext(topStory); //4
Story newestStory = hackerNewsRestAdapter.getNe
westStory();
subscriber.onNext(newestStory);
subscriber.onCompleted(); //5
} catch (JsonParseException e) {
subscriber.onError(e); //6
}
}
}
});
As you can see from the preceding snippet, Observables can be cre
ated from pretty much any operation. The flexibility with which
Observables can be created is another way in which they are like
Iterables. Any object can be made to implement the Iterable
interface, thereby exposing a stream of synchronous data. Similarly,
an Observables data stream can be created out of the work done by
any object, as long as that object is passed into the Observa
ble.OnSubscribe thats used to create an Observable.
At this point, astute readers might wonder whether Observables
really do emit streams of asynchronous data. Thinking about the
previous example, they might wonder to themselves, If the call()
method on the Observable.OnSubscribe function object is typically
called when Observable.subscribe() is invoked and if that method
invokes blocking synchronous methods on the hackerNewsRestAdap
ter, then wouldnt calling Observable.subscribe() block the main
thread until the Observable has finished emitting the Storys
returned by the hackerNewsRestAdapter?
This is a great question. Observable.subscribe() would actually
block the main thread in this case. There is, however, another piece
of RxJava that can prevent this from happening: a Scheduler.
Schedulers
Schedulers determine the thread on which Observables emit their
asynchronous data streams and the thread on which Observers con
sume those data streams. Applying the correct Scheduler to the
An Introduction to RxJava
Observable.create(new Observable.OnSubscribe<Story>() {
//...
}).subscribeOn(Schedulers.io());
5 As I point out in the concluding section of this report, this method belongs to a library
called RxAndroid.
Schedulers
Operators
The Schedulers we discussed in the previous section were passed
into both the Observable.subscribeOn() and Observable.observ
eOn() methods. Both of these methods are operators. Operators
allow us to declaratively compose Observables. In order to get a bet
ter grip on operators, lets briefly break down the phrase declara
tively compose.
To compose an Observable is simply to make a complex Observa
ble out of simpler ones. Observable composition with operators is
very similar to the composition that occurs in function composition,
the building of complex functions out of simpler ones. In function
composition, complex functions are built by taking the output of
one function and using it as the input of another function.
For example, consider the Math.ceil(int x) function. It simply
returns the next integer closest to negative infinity thats greater than
or equal to x . For example, Math.ceil(1.2) returns 2.0. Now, sup
pose we had takeTwentyPercent(double x) , a function that simply
returned 20% of the value passed into it. If we wanted to write a
function that calculated a generous tip, we could compose
Math.ceil() and takeTwentyPercent() to define this function:
double calculateGenerousTip(double bill) {
return takeTwentyPercent(Math.ceil(bill));
}
10
An Introduction to RxJava
thread.
Here we can see that the composition of the Observable is just like
the composition of a function. calculateGenerousTip() was com
posed by passing the output of Math.ceil() to the input of take
TwentyPercent(). Similarly, androidFriendlyStoriesObservable
is composed by passing the output of applying the subcribeOn oper
ator as the input for applying the observeOn operator.
Note that the way in which operators allow us to compose Observa
bles is declarative. When we use an operator, we simply spec
ify what we want our composed Observable to do instead of provid
ing an implementation of the behavior we want out of our com
posed Observable. When we apply the observeOn and subscribeOn
operators, for example, we are not forced to work
with Threads, Executors, or Handlers. Instead, we can simply pass
a Scheduler into these operators and this Scheduler is responsible
for ensuring that our composed Observable behaves the way we
want it to. In this way, RxJava allows us to avoid intricate and errorprone transformations of asynchronous data.
Composing an android friendly Observable that emits its items
on a background thread and delivers those items to Observers on
the main thread is just the beginning of what you can accomplish
with operators. Looking at how operators are used in the context of
Operators
11
12
| An Introduction to RxJava
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
Conclusion
At the beginning of this chapter, I gave a general definition of
RxJava:
RxJava is a library that allows us to represent any operation as
an asynchronous data stream that can be created on any
thread, declaratively composed, and consumed by multiple objects on
any thread.
At this point you should have a good grasp of this definition and
you should be able to map pieces of the definition onto certain con
cepts/objects within the RxJava library. RxJava lets us represent any
operation as an asynchronous data stream by allowing us to create
Observables with an Observable.OnSubscribe function object that
fetches data and notifies any registered Observers of new elements
in a data stream, errors, or the completion of the data stream by call
ing onNext(), onError(), and onCompleted(), respectively. RxJava
Schedulers allow us to change the threads on which the asynchro
nous data streams emitted by Observables are created and
Conclusion
13
14
An Introduction to RxJava
15
destroyed and re-created to load the layout appropriate for the devi
ces new orientation.
This feature of the Android framework requires any effective asyn
chronous data loading solution to have two properties. First, it must
be able to notify an Activity that its data-loading operation is com
plete without causing that Activity to leak. Second, it should not
force developers to re-query a data source just because of a configu
ration change. Rather, it should hold onto and deliver the results of a
data-loading operation to an Activity thats been re-created after a
configuration change. In this section, I show that if RxJava is used
correctly, it can have these two properties and thus, that it can be an
effective data-loading solution for Android apps.
17
Recall that the code running inside of the call() method is running
on an I/O thread. Because of this, we are able to call blocking meth
ods like HackerNewsRestAdapter.getTopStory(), without worry
ing about blocking the UI thread. We can easily imagine a case
where this code starts to run on an I/O thread, but then the user
closes the Activity that wanted to consume the data emitted by this
Observable.
In this case, the code currently running in the call() method is a
GC-root, so none of the objects referenced by the block of running
code can be garbage collected. Because the Observable.OnSub
scribe function object holds an implicit reference to the Activity,
the Activity cannot be garbage collected until the code running in
the call() method completes. Situations like this can be avoided by
ensuring that the Observable.OnSubscribe object is an instance of
a class that does not have an implicit or explicit reference to your
Activity.
18
19
20
21
22
@Override
public boolean onQueryTextSubmit(String query) {
return false;
}
@Override
public boolean onQueryTextChange(String queryText) {
if (queryText.length() > 2) { //2
Message message = Message.obtain(mHandler,
MESSAGE_QUERY_UPDATE,
queryText); //3
mHandler.sendMessageDelayed(message,
QUERY_UPDATE_DELAY_MILLIS);
}
mHandler.removeMessages(MESSAGE_QUERY_UPDATE); //4
return true;
}
});
23
extends
Asyn
24
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<Story>>() {
//...
@Override
public void onNext(List<Story> stories) {
mStoriesRecyclerView.setAdapter(new StoryAdapter(sto
ries));
}
});
25
2 Mary Rose Cook makes a similar point in her Introduction to Functional Program
ming.
3 Kaushik Goupal complains about this in his talk Learning RxJava (for Android) by
Example.
26
execute a search against that query string. With this new feature, a
search would be executed if there was a 100 millisecond delay after
any characters had been changed in a query string that was at least
three characters long and if the user tapped one of her past search
query strings. Now, suppose that we want to be able to measure how
useful these history-based search suggestions are for our users by
tracking their usage with analytics.
In this case, we would still want our stories list to be updated when
ever the results from a search have been returned. The only thing we
need to change is the conditions under which a search is
executed. To do this, we need an Observable that emits the query
string of any of the tapped search suggestions.4 Once we have that
Observable, we can compose it into our data stream by adding an
additional link in our chain of operators:
searchViewTextObservable.filter(new Func1<String, Boolean>() {
//...
})
.debounce(QUERY_UPDATE_DELAY_MILLIS, TimeUnit.MILLISECONDS)
.mergeWith(historicalQueryTappedObservable)
.flatMap(new Func1<String, Observable<List<Story>>>() {
//Returns Observable that represents the async data
returned from a network call
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<Story>>() {
//...
});
4 I realize that getting an Observable that does this is not trivial, but discussing how this
would be done in detail would take us too far off topic. The main point here is just to
show off RxJavas flexibility.
27
28
});
historicalQueryTappedConnectableObservable.subscribe(new
Observer<String>() {
//Log tap for analytics
});
historicalQueryTappedConnectableObservable.connect();
Conclusion
In this chapter, you saw why you should consider using RxJava in
your Android code. I showed that RxJava does have two properties
that are essential for any effective asynchronous data-loading solu
tion. It can load asynchronous data into an Activity
without leaking that Activity
without forcing developers to re-query a data source simply
because of a configuration change.
I also compared an implementation of a feature that utilized the
standard classes for handling asynchronous data with an
RxJava-based implementation and tried to say a little about why
RxJava-based implementations are often cleaner and more flexible
than standard implementations.
Conclusion
29
31
33