A lot of Android programmers are now exploring Kotlin, a programming language based on Java and the JVM that is used to develop Android applications. As is often the case with Java-based Android apps, one of the main problems when approaching Android development in Kotlin is the management of asynchronous code. In Java, this has been solved by the AsyncTask
class, or the runOnUiThread
method. In Kotlin, something different is required.
At Codemotion Milan 2018, Fabio Collini, Google Developer Expert and Senior Android Developer, delivered a speech on how to implement asynchronous code in Kotlin. Specifically, he described two different approaches: the first using coroutines, the second based on a library named ReactiveX Java (or RxJava in short). Both of these approaches allow users to implement and support asynchronous operations, and can be also used both together and interchangeably.
This post provides a quick overview on how these approaches differ from each other.
Before starting, let’s try to understand more clearly the main differences between RxJava and coroutines, and how they can help Kotlin programmers.
What are coroutines?
The concept of coroutine is not strictly related to Kotlin. Coroutines are in fact components that generalize subroutines for non-preemptive multitasking, which allows tasks to suspend and resume their own execution. These components are therefore particularly useful for multi-thread programming, and are also found in other languages such as Python or C#.
In Kotlin, coroutines are implemented within the kotlinx.coroutines
library, originally developed by JetBrains. The Kotlin official documentation provides an extensive ‘how to’ guide that is available at this link.
What is RxJava?
RxJava, shorthand for ‘ReactiveX for Java‘, was created by Ben Christensen and David Karnok, with the first version released in November 2014. RxJava has found several applications in the Android world, thanks to its unique features. Indeed, this library allows easy termination of subscription when an activity is ending, thus avoiding memory leaks and the waste of resources derived from working on UI elements that are no longer visible.
Moreover, RxJava is considered one of the most fruitful methods for enabling Reactive Programming in Android development. As we’ll see below, the result is simplified implementation of concurrency and the asynchronous tasks inherent in mobile programming.
Although it is also available for Java,this article will focus on how to use RxJava to implement mobile apps in Kotlin.
A sample service in Kotlin
Fabio Collini started the discussion about using RxJava or coroutines by showing a sample interface for a service aimed at obtaining information from StackOverflow. Since such a service is made up of asynchronous methods, it is perfect for our purposes.
The first question is therefore how to define methods that are asynchronous? With RxJava, we need to use a Single
class as type for the return value:
interface StackOverflowService { @GET("/users") fun getTopUsers(): Single<List<User>> @GET("/users/{userId}/badges") fun getBadges( @Path("userId") userId: Int ): Single<List<Badge>> @GET("/users/{userId}/top-tags") fun getTags( @Path("userId") userId: Int ): Single<List<Tag>> }
This allows use of the subscribe
method in order to wait for the response.
With coroutines, there are two possibilities: the first is similar to the above, but uses the Deferred
class:
interface StackOverflowService { @GET("/users") fun getTopUsers(): Deferred<List<User>> @GET("/users/{userId}/badges") fun getBadges( @Path("userId") userId: Int ): Deferred<List<Badge>> @GET("/users/{userId}/top-tags") fun getTags( @Path("userId") userId: Int ): Deferred<List<Tag>> }
However, a recent upgrade of Kotlin allows for declaration of suspending functions, which in practice makes the code appears synchronous, even it is actually the opposite:
interface StackOverflowService { @GET("/users") suspend fun getTopUsers(): List<User> @GET("/users/{userId}/badges") suspend fun getBadges( @Path("userId") userId: Int ): List<Badge> @GET("/users/{userId}/top-tags") suspend fun getTags( @Path("userId") userId: Int ): List<Tag> }
In what follows, we will consider the latter approach, which makes the code cleaner.
Using service methods
Now, if we want to use the aforementioned service, RxJava and coroutines require different approaches. As mentioned above, RxJava requires using the subscribe
method:
class MyViewModel( private val service: StackOverflowService ) : ViewModel() { private val disposable = CompositeDisposable() fun load() { disposable += service.getTopUsers() .subscribeOn(io()) .observeOn(mainThread()) .subscribe( { users -> updateUi(users) }, { e -> updateUi(e) } ) } private fun updateUi(s: Any) { //... } override fun onCleared() { disposable.clear() } }
The subscribe method accepts two parameters: the first is the one we want to execute, while the second (e -> updateUi(e)
) is used in case of errors.
While RxJava is synchronous by default (one consequence of this is that we resorted to subscribe), using coroutines and suspending functions allows us to write code that is automatically interpreted as asynchronous, even if it appears synchronous. To understand this difference, compare the following snippet with the previous one: they are equivalent in functionality.
class MyViewModel( private val service: StackOverflowService ) : ViewModel() { fun load() { viewModelScope.launch { try { val users = service.getTopUsers() updateUi(users) } catch (e: Exception) { updateUi(e) } } } private fun updateUi(s: Any) { //... } }
Here, viewModelScope
simplifies the code, even if it is not available yet using Kotlin. However, an implementation is provided in this link.
In general, even more complicated things are made possible by using coroutines or RxJava. Additional code examples by Fabio Collini are available on GitHub.
Coroutines or RxJava?
Having read this quick overview of coroutines and RxJava, one might ask which solution is the best. In terms of number of methods, RxJava is bigger than a solution based on coroutines only. However, this difference can be eliminated by using Proguard and its code optimization. The following graphs taken from Fabio Collini’s slides show this.
Conclusions
So, which approach should we use for production code?
In order to answer such a question, Fabio Collini proposed the following ‘algorithm’:
- if you are already using RxJava and it works for you, then use RxJava
- if the architecture is based on a reactive stream, then use RxJava
- if the project is multiplatform with Kotlin Native, then use coroutines
- if the codebase is Java/Kotlin, then use RxJava
- otherwise, use coroutines
An additional note: consider that coroutines are generally easier to understand, while the learning curve for RxJava is steep. This makes coroutines generally preferable, except in the aforementioned cases.