In this article, I will try to explain coroutines in Android in what I call the “easy way.”
When writing code that involves database transactions, network access, or any other expensive interaction, you might want to write async code and just provide a callback that gets executed after the expensive function is done. This isn’t a completely terrible idea because your code would run asynchronously and the callbacks would get executed on the main thread (as it should be). Right?
On second thought, it’s a terrible idea because there is a high chance that there will be memory leaks with this approach. To prevent the OutOfMemoryError
from happening, we would need to cancel all transactions when the activity is destroyed (which is cool until you need to handle a lot of things concurrently). In addition, callbacks also don’t allow the use of some features (e.g., exceptions).
Coroutines help by reducing the need for callbacks in our async code. Look at the sample async code snippet below:
fun getDetails(){fetchApiData { res ->saveToDb(res) { rows ->// ...}}}
As stated earlier, we are trying to avoid memory leaks and coroutines help with that by allowing us to write sequential code. See below:
suspend fun getDetails(){val res = fetchApiData()saveToDb(res)}...suspend fun fetchApiData(){ /* Code here */ }
When using coroutines, that is not the case, and here’s why. The suspend
keyword on the coroutine function makes all the difference. With coroutines, the fetchApiData()
function is called (on the main thread). However, similar to callback, the main thread isn’t blocked and fetchApiData()
still runs on a different thread. When the response from fetchApiData()
has been received, the coroutine is resumed. Basically, the coroutines library handles all these things for us without the need to create multiple callbacks. If you ask me, this makes the code much easier to read.
Coroutines work with the suspend and resume mechanism. When a suspend function is called, Kotlin needs to keep track of the fact that it’s running a coroutine as opposed to a regular function. All of this happens on the call stack. When getDetails()
is reached, it is called like a normal function and begins execution until it finds another suspend function (fetchApiData()
). When this happens, the compiler transforms the suspending functions into a state machine.
When all the coroutines on the main thread are suspended, the main thread is free to do all other work so that our users can still successfully interact with our app while things are going on in the background. Once the result from a suspended function is ready, the function is resumed. For this to happen, it takes the saved state back to the stack and resumes the function.
In using coroutines, we would be sure to never block the main thread when writing our code so that our apps won’t freeze or create a not so good experience for our users.
First, go to the build.gradle
file and include these libraries.
dependencies {...implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:x.x.x"implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:x.x.x"}
x.x.x signifies the coroutines library version (you should add the most recent version).
Once these libraries have been added to your Android project, you can write simple coroutine functions (similar to the one written above).
If you’re familiar with async
and await
in JavaScript and Ruby, then that’s a good thing because the pattern of async
and await
is based on coroutines. In Kotlin, the suspend
keyword is similar to async
. However, await
is implicit when calling a suspend
function in Kotlin.