London Tube Status App — from Rx to Coroutines

Index:
- London Tube Status App — setting up
- London Tube Status App — gradle cleanup and dagger
- London Tube Status App — Retrofit and API setup
- London Tube Status App — wrapping up the data layer
- London Tube Status App — domain layer
- London Tube Status App — layouts and view model
- London Tube Status App — MVP implementation
- London Tube Status App — UI tests and going live
- London Tube Status App — from dagger to koin
- London Tube Status App — from MVP to MVVM
- London Tube Status App — from Rx to Coroutines
Hi everyone, welcome to another article on my London tube app series. Guess what? Another migration guide. Now that Coroutines are stable, I decided to migrate this app from Rx to Coroutines to give it a try.
The app we’ve been working on is relatively simple with a single API call where the only operations we apply on the data are the mappings between the clean architecture layers. According to most people, this makes it a good candidate to try Coroutines as Rx is not really needed for a simple example like this
Let’s do it then
As expected, starting with dependencies, let’s update our dependencies files as below with the new dependencies and versions:
We need to add dependencies for Coroutines Android and Core and also the CoroutinesAdapter to use with Retrofit. Then we need to go to our common.gradle
and data_build.gradle
scripts respectively and add the following lines:
We add the Coroutines dependencies in the common.gradle
script so it’s available in all modules, and then we add the CoroutinesAdapter just in the data_build.gradle
script as we don’t need this dependency elsewhere.
Note: I also upgrade the Kotlin version to 1.3.0 otherwise we couldn’t use the stable version of Coroutines. Since we have a really simple use case, I also went ahead and deleted all the Rx dependencies. I decided to delete them to compare APK sizes after the migration, more on this later.
Network layer
Moving on to the code, let’s start from the network side and move along until we get to the presentation layer and our view model. Our activity code won’t even be touched. The first thing to do is go to our NetworkModule
and add the CoroutinesCallAdapterFactory
to our Retrofit builder as in line 11 below:
Left the rest of the code out for brevity. Moving on to our StatusService
and StatusRepository
implementation:
A few changes here, let’s look at them. First, in the service, we simply dropped Rx Single and we now wrap our network model in a Deferred object. This is basically a non-blocking cancellable future, which means that the function is asynchronous and the job that triggered it can be cancelled.
In the repository implementation, in order for it to be able to call our updated StatusService
interface, we need to add the suspend
keyword to our getLinesStatus()
function. Then in order to actually read the data from our Deferred we just need to call the function and add .await()
in the end.
As this is a network call it can obviously fail, so we need to wrap that line with a try/catch block in order to catch any exceptions. To cover this use case I also created a new StatusResult
sealed class to represent the result of the API call and propagate the error forward to the other layers when that happens.
Domain layer
Our repository no longer returns an Observable as we can see above, and now returns our newly created StatusResult
wrapper straight away. That basically means our GetLinesStatusInteractor
is now really basic and doesn’t really do anything apart from propagating this to our view model:
We no longer handle subscribeOn()
and observeOn()
here anymore since we dropped Rx so no need to inject the RxSchedulers
here anymore. In fact, we don’t need those at all anymore as we’ll see when looking at the view model code, so I deleted them completely from the codebase.
Presentation layer
And to finish, let’s see our StatusViewModel
looks like now. As promised we didn’t touch any code on the activity, all the presentation layer changes are in this file:
This is probably where we have more changes so let’s look at them one by one:
- Our view model now also implements
CoroutineScope
so it allows to uselaunch
as in line 14 without any prefixes as the default scope implementation is already defined. - That’s basically done on line 7 where we define the default
CoroutineDispatcher
to use. We also keep a reference to aJob
so we can cancel it where we used to clear our disposables, on line 32. - Then on line 14 we start our
launch
block. The code inside this block is going to be run in the main thread since that’s the one we defined as the default dispatcher on line 8. - In order to call our interactor outside of the main thread, we wrap that call in a
withContext(Dispatchers.IO)
as we can see on line 15.
APK size
And this is all we have to do, the app is now fully using Coroutines and no more Rx. For fun and because I actually didn’t know the size of the Rx or Coroutines dependencies and the impact they would have on the APK size, I decided to compare the APK before and after, here are the numbers:
With Rx: APK size: 3.4 MB
With Coroutines: APK size: 3.2 MB
Again, I don’t think one replaces the other all together but the difference is actually quite small anyway, so APK size I would say is not a reason to migrate from Rx to Coroutines, especially considering that most likely you’re keeping both if you have a relatively big size project.
Note: this comparison was made with debug
APKs not using Proguard or Dexguard, so nothing obfuscated or minified.
Conclusion
As mentioned by other people, the code for this kind of example is quite clean and easy to read, but to be honest for such a simple example I also don’t find the Rx code hard to write or read. So, for me, unless I would have another big reason to move away to Coroutines in a bigger project, that task would probably not live on the top of the backlog. That said, it was a nice experience and everyone should definitely give it a try and see how it impacts their programming experience and go from there.
And that’s all for today and for 2018. This was the last article of the year, I hope it’s a nice closure. If you enjoyed it please give some 👏 and see you back in 2019 👋.
London Tube Status App — from MVP to MVVM ⬅ PREVIOUS
Extra note: We’re not (yet at least) using Coroutines at Babylon Health but we do love Kotlin. If you also do and want to help us build the future of healthcare, we are currently hiring to extend our Android team. Check out the job spec below or DM me for more info: