RxJava — The first 3 patterns

If you’re an Android developer and you’ve heard over the past couple of years a lot about RxJava but haven’t tried it yet, or perhaps you’re new to Android development and you’re looking for a practical example on where to start with RxJava, this article is for you.

What I hope to cover are the first 3 patterns you’ll need when building an Android app and choosing to use RxJava as part of your architecture, specifically around making network requests to an API.

This article will focus on RxJava 2 as that is the latest version (as of Jun 2017) and we’ll start with a basic intro to RxJava and some of the components I’ll be using to demonstrate the patterns. For my networking layer I’m using Retrofit and for my cache I have a simple in-memory HashMap, this could easily be replaced by Room or another database implementation.

My Retrofit interface has simple methods like this one, which fetches a list of Events.

@GET("events")
Single<List<Event>> getEventsFeed(...);

This is exposed via my Repository interface and we subscribe to it like this

Single<List<Event>> source =
remoteRepository.getEventsFeed(...);
source
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(data -> {
// Do something with data e.g. pass it to a view.
},
throwable -> {
// Handle error e.g. get view to show dialog.
}
);

The above code shows us making a simple network request by subscribing to an Observable (a Single in this case that should return a single result which is the list of events). We also define that we’d like to run the network request on an “io” thread so that the main UI thread stays responsive. We observe the results of the network request on the Android main thread so that we can update any view components without having an exception thrown.

In the examples below we’re just going to be changing how the “source” is defined, the rest of the code works in exactly the same way, this is one of the advantages of RxJava, you can chain complex async tasks together but the code to actually execute and observe the results stays the same.


Now on to the 3 patterns I think you’ll need for any basic or medium complexity Android app.

  1. Fetching from a cache or network.
  2. Making 2 requests where the second depends on the first.
  3. Making multiple requests simultaneously and combining the results.

Fetching from a cache or network

Get me a value from a cache if it exists, otherwise fetch it from the network

Maybe<List<Event>> source1 =
cacheRepository.getEventsFeed(...);
Single<List<Event>> source2 =
networkRepository.getEventsFeed(...);
Maybe<List<Event>> source = 
Maybe.concat(source1, source2.toMaybe()).firstElement();

Here we use the concat operator which will join 2 other observables together, however the use of the firstElement means we only care about the first result that is emitted. So if the cache has a value, this will be emitted and onComplete is called and this is the value returned. The network call won’t even be made in this instance which is what we’d expect. If however the cache doesn’t contain a value, then it will onComplete without having emitted a value, therefore the network request will be made. The use of the Maybe Observable here indicates that potentially there may be no value to observe e.g. if the cache is empty and no results are returned from the network.

http://reactivex.io/documentation/operators/concat.html

http://reactivex.io/documentation/operators/first.html

Making 2 requests where the second depends on the first

Get me a value from the network and using some part of that result, make another network request to get me the actual data I wanted.

Single<User> source1 =
networkRepository.getMyProfile(...);
Single<List<Tweet>> source = source1.flatMap(user -> {
return networkRepository.getUserTweets(user.getTwitterId());
});

In this example we first fetch the users profile from the network using some previously stored access token. Then using a field on the returned User object we fetch the user’s tweets from another network call. The flatMap operator here lets us transform the Observable from the first request by extracting the user object and then mapping that to the second request which creates the final Observable. If you want to return the first result and the second result at the same time, when the second Observable ends, just return a Pair<User, List<Tweet>> object.

http://reactivex.io/documentation/operators/flatmap.html

Making multiple requests simultaneously and combining the results.

I have 3 network requests to make, they’re not dependant on each other to execute, so I want to execute them all at the same time to improve response time. But I don’t want to emit a result until all 3 requests have finished.

Single<List<Event>> source1 =
networkRepository.getEventsFeed(...);
Single<List<Bookmark>> source2 =
networkRepository.getBookmarks(...);
Single<Stats> source3 =
networkRepository.getUserStats(...);

Single<MyViewModel> source =
Single.zip(source1, source2, source3, MyViewModel::new);

In this example we have 3 Observables that we combine using the zip operator. They’re combined into a POJO called MyViewModel which has constructor arguments that match the types of the source Observables

public MyViewModel(List<Event>, List<Bookmark>, Stats) {...}

When you subscribe to the source Observable all 3 network requests will be made at the same time, but only once they’ve all completed will the new MyViewModel instance be emitted.

http://reactivex.io/documentation/operators/zip.html


I think you’ll agree that the 3 patterns above are pretty common when it comes to building Android apps, I know I’ve used one or more of them in several projects recently. The really cool thing about RxJava and Observables is that you can mix and match these into quite complex ways, but still maintain readability in your code.

For example, you could combine the first pattern and the last one, so that each of the 3 Observables to be combined using the zip operator could also be coupled with a local cache, so some calls might return immediately from the cache, others may come from the network (particularly if they all had different cache timeout values).

Special thanks to Hugo Visser for proof-reading this article.

I’d love to hear you feedback in the comments, or drop me a message on Twitter.