Most Android apps rely on network calls to a set of backend services. As an app grows, so does the complexity of network calls and data operations. Networking libraries like Retrofit and Volley provide all the functionality needed for basic API calls. However, for threading and sequencing of events beyond a single API call, you may end up writing messy, error-prone code. Enter RxJava, an extension of the Observable pattern that abstracts away the complexities of threading and synchronization and integrates seamlessly with Retrofit.
At DoorDash, we’ve experimented with RxJava and found that it has tremendous benefits. In this blog post, we’ll talk about how using RxJava allows us to handle network operations in a reliable, scalable way.
Let’s start off with a basic example in the DoorDash consumer app that makes an asynchronous API call via Retrofit to fetch the list of restaurants at a given location. Using Retrofit’s Callback<T> interface, we perform the request on a background thread and get the response in onResponse on the main thread.
https://gist.github.com/137dbbd8e4a58884c8c96f146ad11974
While this approach works fine for this basic use case, it does not scale much beyond that. Let’s consider a requirement change.
Chaining API Calls
Let’s say the requirement now changes to fetch restaurants based on the user’s default address on login. For this, we have to first make the API call to fetch the user info and then fetch the restaurant list. Using the previous approach, this is what it would look like:
https://gist.github.com/c63033d011bd498317d68378d0753504
Let’s look at the problems in this code:
- It’s not readable because of multiple nested callbacks
- It’s not trivial to make the API call aware of lifecycle changes, which can lead to crashes when updating the view
- The API call to fetch user info is tightly coupled to the call that fetches the restaurant list
Let’s see how RxJava helps us with this problem:
https://gist.github.com/6daffe76b538bea24ee5989f6875ef0d
It addresses the above issues in the following manner:
- Makes the code clean and readable
- The fragment can subscribe to the Observable, which makes the API calls on a background thread and emits data on the main thread, as specified with subscribeOn and observeOn. It can unsubscribe from the Observable to stop receiving data from it, as done in onPause
- Adding API calls to the chain is easy, without much overhead
Making Parallel Requests
Let’s say we want to run an A/B test to display photos for the fetched restaurants. Let’s assume we have an API call that returns a boolean value of the experiment for the current user, where “true” means we should show restaurant photos in the list. Using the same approach from before, we could just use another flatMap operator to first fetch the restaurants and then fetch the experiment value to display the correct experience based on the experiment value. However, ideally we should make the calls in parallel since they are independent of each other and combine their results.
Let’s see how we can do this with the the zipWith operator:
https://gist.github.com/2c59b83b8c777e1fc31c4d01f27a09ea
RxJava comes with a powerful set of operators that can be used to compose sequences together. For this use case, we make the two API calls in parallel and combine their results using the zipWith operator before emitting the final result.
Conclusion
Hopefully these examples showed you that RxJava can help you easily adapt to the changing needs of your app via it’s APIs and operators. Our Android apps have benefited from using RxJava for solving problems such as combining data streams, search-as-you-type, and polling. Let us know if you have additional thoughts on how RxJava can help with your applications or come join the team.