I exchanged RxJava for coroutines in my Android application. Why you probably should do the same

RxJava is like a bazooka and most applications don’t use half of its firepower. Here’s how to change it to coroutines

Paulo Sato
ProAndroidDev

--

Update

Kotlin 1.3 is out and with it Coroutines are not experimental anymore. I update my repo with a coroutine_v1 branch and updated my base Usecase.

Also I updated the chart comparing executing time, you can see that there was performance improvement in the V1 release

Comparison with V1

I’ve been working with RxJava for years. It’s arguably one of the best libraries for any Android project and nowadays it still has the punch, especially if you’re using Java for developing. However, if you’re using Kotlin, we can say that there’s a new sheriff in town.

Most people that uses RxJava do so just to control threads and to avoid callback hell (if you don’t know what callback hell is, consider yourself lucky. Here’s why). The point is: we need to have in mind that the real power of RxJava is reactive programming and back pressure. If you’re using it to control async requests, you’re basically using a bazooka to kill a spider. It will do the job, but it’s complete overkill.

One significant disadvantage of RxJava is the method count. It is HUGE and, if you use it, it tends to spread throughout the code. With Kotlin, you can use coroutines to implement most of every behavior that you have created using RxJava.

But…what are coroutines?

A coroutine is a way to handle concurrency tasks in a thread. It will run until stopped, and the thread will change the context to each coroutine without creating new threads.

Coroutines for Kotlin are still experimental, but it is graduating on Kotlin 1.3, so I wrote below a new UseCase class (for clean architecture) using them. In this example, the coroutines call will be encapsulated in a single file. By doing so, other layers will not depend on coroutines to run, delivering a more decoupled architecture.

First of all, I created a parent job. This is crucial to cancel all coroutines that are created on this UseCase. When we call execute, it is important to cancel the old job just to make sure that we don’t leak any coroutine (this also happens if we unsubscribe from this UseCase).

I also called launch(UI). This means that I want to create a coroutine that will run on the UI thread. After that, I called the background method, which creates an async on CommonPool (this will actually have a poor performance). The async, in its turn, will return a Deferred, and then I call its await method. It waits for the background coroutine to finish, bringing a result or an exception.

This can be used to implement most of what we do with RxJava. Below, some examples:

Map

I downloaded the searchShow result and changed it to return the first show name.

RxJava
Coroutines

Zip

A Zip will take two emissions from an Observer and put then together in a new emission. Note that, with RxJava, you have to tell it to execute the call in parallel using the subscribeOn on each Single. We want to get both at the same time and return it together.

RxJava
Coroutines

FlatMap

In this case, I’m searching for shows that have the query String, and for each result (limited to 200) I’m also getting the rating for each show. In the end, it returns a list with every show and its respective rating.

RxJava
Coroutines

Allow me to explain. With RxJava code, my repository will return a single emission of a List<ShowInfo>, so I need to have several emissions, one for each ShowInfo. To do so, I called flatMapPubliser. For each emission, I need to emit a ShowResponse and, lastly, gather all in a list.

We end up with something like this: List<ShowInfo> foreach → (ShowInfo → ShowRating → ShowResponse) → List<ShowResponse>.

With Coroutine, I did a map for each member of the List<ShowInfo> to convert to a List<Deffered<ShowResponse>> and then I converted to a List<ShowResponse>.

As you can see, most of what’s done with Rxjava is easier to implement working with synchronous calls. Coroutines can handle even flatMap, which I believe is one of the most complex functions in RxJava.

Coroutines are well known to be very light (here’s an example), but the results puzzled me. In this example, RxJava took average 3.1 seconds to run, while coroutines took average 5.8 seconds to run on CommonPool.

This left me questioning if there's something misplaced; later I found out what it was. I was using retrofit Call, which blocks the thread.

To fix this issue we have two options, depending on which Android Studio you’re using: on Android Studio 3.1, we need to make sure we are not blocking the background thread. To do this I used this library:

implementation 'ru.gildor.coroutines:kotlin-coroutines-retrofit:0.12.0'

This creates an extension function for retrofit Call to suspend the thread

Source: https://github.com/gildor/kotlin-coroutines-retrofit

On Android Studio 3.2, you can update the coroutines library to version 0.25.0. This version have the CoroutineContext IO (you can see it commented on my UseCase class).

Running on CommonPool without blocking call it took 2.3 seconds and 2.4 with IO with blockings calls.

A chart comparison for the time to run with RxJava, Coroutines on CommonPool Blocking , CommonPool suspending, and Coroutines on IO

I hope that this article inspires you to try coroutines, a lighter and maybe faster alternative to RxJava, and a lot easier to understand because you’re writing synchronous code that runs asynchronously.

Since a lot of you guys asked, this is the code that I used to generate this benchmarks, keep in mind that the code was for tests only and was never meant to go to production.

--

--