Kotlin coroutines are the new way to manage async operations on Android. Since their release last year, they have become quite trendy in the development world. So it’s worth exploring them in detail in a series of posts.
What are they?
Coroutines are basically functions that can be suspended and resumed. So, internally, they behave as state machines. When suspended, its state is saved. When resumed, its state is restored, so they can continue their execution from the suspension point. In between, they free the resources are using, so execution is never blocked.
Main advantages
- Cleaner code when managing background operations (no callbacks required, at first look the statements seem to be sequential code)
- Safer code when calling background operations from the main UI thread
Things to keep in mind…
- Coroutines are often defined as light-weight threads. However, this definition can be misleading: coroutines do not replace threads. In fact, both threads and coroutines work together, because coroutines run on threads.
- Coroutines are synchronous by default (running on the main thread), although they can be configured to run in background.
- As we said before, when a coroutine is in suspension state, the thread that the coroutine is running on is free to perform other tasks.
- Coroutines are generally used to write asynchronous code that looks a little more like synchronous code. They are based on the “async and await” pattern.
“Coroutines are functions that can be suspended and resumed in a non-blocking way”
Creating coroutines
To launch a new coroutine, we need a coroutine builder. These builders are the entry points from which we can start our coroutines. Some of the builders available are:
- launch {}
- async {}
- runBlocking {}
- runBlockingTest {}
launch {
// coroutine code goes here...
doSomeWork()
doSomeMoreWork()
}
Inside these blocks, all actions will be executed sequentially, even the asynchronous tasks. That’s the “magic” behind coroutines.
Optionally, builders can take a parameter to specify the execution thread:
// actions will be performed on specific background thread for IO operations
launch(Dispatchers.IO) {
doSomeWork()
...
}
“Builders are the main entry points for coroutines and suspension code”
Coroutine builders are, in fact, extensions from the coroutine scope. The scope:
- controls the lifecycle of a group of coroutines using an internal job
- specifies a dispatcher to define on which thread coroutines are running on
By default, we can use the GlobalScope class to work our coroutines. But we can also build a custom coroutine scope using a configuration object (also called coroutine context):
// job to manage coroutines life
val coroutineJob = Job()
// context to wrap all coroutine configuration: its job, its dispatcher. Operator "+" is overloaded and returns a context
val coroutineContext = Dispatchers.Main + coroutineJob
// scope built using context
val customCoroutineScope = CoroutineScope(coroutineContext)
Cancelling coroutines
Coroutines must be cancelled, otherwise they would continue their execution and leak resources. Cancellation is done using the coroutine scope job:
coroutineJob.cancel()
Manual management of coroutine lifetime is error-prone, but there is an alternative that does all the heavy-lifting for us…
Viewmodel scope
The Android Architecture Components library contains this built-in class which provides automatic coroutine lifecycle management.
So every viewmodel has available a property called viewmodelScope (through inheritance) that can be used to launch coroutines:
viewmodelScope.launch {
doSomeMoreWorkFromVm()
}
All coroutines launched using this scope will be automatically cancelled when viewmodel is no longer in use:
override fun onCleared() {
super.onCleared()
job.cancel()
}
“Class ViewModelScope manages automatically the coroutines lifecycle for us”
Switching contexts
Inside any coroutine, we can easily switch the thread we’re running on:
launch(Dispatchers.MAIN) {
// code executed on main thread...
someAction()
anotherAction()
// SWITCH!
withContext(Dispatchers.IO) {
// code executed on back thread
someBackgroundAction()
}
// back to main
finalAction()
}
Coming up next…
We will continue this series writing about with suspending functions. However, if you are looking for a full working sample, check out the following repo:
https://github.com/begomez/CoroutinesGarden
References:
https://kotlinlang.org/docs/tutorials/coroutines/async-programming
https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/