https://www.xda-developers.com/files/2019/11/android_kotlin-1.jpg

Kotlin Coroutines [Part-1]

Bharath Kandula
Tilicho Labs
Published in
3 min readJan 4, 2022

--

kotlinx.coroutines is a rich library for coroutines developed by JetBrains. It contains a number of high-level coroutine-enabled primitives that this guide covers, including launch, async , coroutine contextand dispatchers.

This is a guide on the core features of kotlinx.coroutines a series of examples, divided up into different topics.

What is a Coroutine?

A coroutine is an instance of suspendable computation. It is conceptually similar to a thread, in the sense that it takes a block of code to run that works concurrently with the rest of the code. However, a coroutine is not bound to any particular thread.

launch { // launch a new coroutine and continue
delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
println("World!") // print after delay
}
println("Hello") // main coroutine continues while a previous one is delayed
}

Output:
Hello
World!

Each coroutine has it’s own CoroutineScope and CoroutineContext.
CoroutineContext can be from parent to child, the child inherits the properties of parent coroutine.

launch is a coroutine builder.
delay is a special suspending function.

runBlocking is also a coroutine builder that bridges the non-coroutine world of a regular fun main() and the code with coroutines inside of runBlocking { ... } curly braces. This is highlighted in an IDE by this: CoroutineScope hint right after the runBlocking opening curly brace.
If you remove or forget runBlocking in this code, you'll get an error on the launch call, since launch is declared only in the CoroutineScope.
Unresolved reference: launch

Extract function refactoring

Let’s extract the block of code inside launch { ... } into a separate function. When you perform "Extract function" refactoring on this code using Android Studio, the official Android development IDE, you get a new function with the suspend modifier.

Suspending functions can be used inside coroutines just like regular functions, but their additional feature is that they can, in turn, use other suspending functions (like delay in this example) to suspend execution of a coroutine.

import kotlinx.coroutines.*

fun main() = runBlocking { // this: CoroutineScope
launch { doWorld() }
println("Hello")
}

// this is your first suspending function
suspend fun doWorld() {
delay(1000L)
println("World!")
}

Output:
Hello
World!

Scope builder

The runBlocking and coroutineScope builders may look similar because they both wait for their body and all its children to complete. The main difference is that the runBlocking method blocks the current thread for waiting, while coroutineScope just suspends, releasing the underlying thread for other usages. Because of that difference, runBlocking is a regular function and coroutineScope is a suspending function.

fun main() = runBlocking {
doWorld()
}

suspend fun doWorld() = coroutineScope { // this: CoroutineScope
launch {
delay(1000L)
println("World!")
}
println("Hello")
}

Output:
Hello
World!

Scope builder and concurrency

The coroutineScope builder can be used inside any suspending function to perform multiple concurrent operations. Let’s launch two concurrent coroutines inside a doWorld suspending function:

// Sequentially executes doWorld followed by "Done"
fun main() = runBlocking {
doWorld()
println("Done")
}

// Concurrently executes both sections
suspend fun doWorld() = coroutineScope { // this: CoroutineScope
launch {
delay(2000L)
println("World 2")
}
launch {
delay(1000L)
println("World 1")
}
println("Hello")
}

Both pieces of code inside launch { ... } blocks execute concurrently,
with World 1 printed first, after a second from start, and World 2 printed next, after two seconds from start. A coroutineScope in doWorld completes only after both are complete, so doWorld returns and allows Done string to be printed only after that:

Output:
Hello
World 1
World 2
Done

An explicit job

A launch coroutine builder returns a Job object that is a handle to the launched coroutine and can be used to explicitly wait for its completion. For example, you can wait for the completion of the child coroutine and then print “Done” string:

val job = launch { // launch a new coroutine and keep a reference to its Job
delay(1000L)
println("World!")
}
println("Hello")
job.join() // wait until child coroutine completes
println("Done")

Output:
Hello
World!
Done

Reference:

--

--