With Kotlin 1.3 we finally have a stable library for coroutines. This means that there won’t be any breaking changes to the API. Now is an especially good time to learn how to use them.

Coroutines are basically light-weight, much more efficient threads. As a bonus, they are extremely easy to work with once you know the basics. Asynchronous coroutine code looks the same as a classic synchronous code. You don’t need to learn any new programming paradigms in order to use Kotlin’s coroutines.

In this tutorial you are going to learn quite a bit about coroutines – different ways of launching them, blocking versus non-blocking code and async / await.

?Get the code from this tutorial?

Or get the code here:

import kotlinx.coroutines.*
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors

fun main(args: Array<String>) {
    exampleWithContext()
}

suspend fun printlnDelayed(message: String) {
    // Complex calculation
    delay(1000)
    println(message)
}

suspend fun calculateHardThings(startNum: Int): Int {
    delay(1000)
    return startNum * 10
}

fun exampleBlocking() = runBlocking {
    println("one")
    printlnDelayed("two")
    println("three")
}

// Running on another thread but still blocking the main thread
fun exampleBlockingDispatcher(){
    runBlocking(Dispatchers.Default) {
        println("one - from thread ${Thread.currentThread().name}")
        printlnDelayed("two - from thread ${Thread.currentThread().name}")
    }
    // Outside of runBlocking to show that it's running in the blocked main thread
    println("three - from thread ${Thread.currentThread().name}")
    // It still runs only after the runBlocking is fully executed.
}

fun exampleLaunchGlobal() = runBlocking {
    println("one - from thread ${Thread.currentThread().name}")

    GlobalScope.launch {
        printlnDelayed("two - from thread ${Thread.currentThread().name}")
    }

    println("three - from thread ${Thread.currentThread().name}")
    delay(3000)
}

fun exampleLaunchGlobalWaiting() = runBlocking {
    println("one - from thread ${Thread.currentThread().name}")

    val job = GlobalScope.launch {
        printlnDelayed("two - from thread ${Thread.currentThread().name}")
    }

    println("three - from thread ${Thread.currentThread().name}")
    job.join()
}

fun exampleLaunchCoroutineScope() = runBlocking {
    println("one - from thread ${Thread.currentThread().name}")

    val customDispatcher = Executors.newFixedThreadPool(2).asCoroutineDispatcher()

    launch(customDispatcher) {
        printlnDelayed("two - from thread ${Thread.currentThread().name}")
    }

    println("three - from thread ${Thread.currentThread().name}")

    (customDispatcher.executor as ExecutorService).shutdown()
}

fun exampleAsyncAwait() = runBlocking {
    val startTime = System.currentTimeMillis()

    val deferred1 = async { calculateHardThings(10) }
    val deferred2 = async { calculateHardThings(20) }
    val deferred3 = async { calculateHardThings(30) }

    val sum = deferred1.await() + deferred2.await() + deferred3.await()
    println("async/await result = $sum")

    val endTime = System.currentTimeMillis()
    println("Time taken: ${endTime - startTime}")
}

fun exampleWithContext() = runBlocking {
    val startTime = System.currentTimeMillis()

    val result1 = withContext(Dispatchers.Default) { calculateHardThings(10) }
    val result2 = withContext(Dispatchers.Default) { calculateHardThings(20) }
    val result3 = withContext(Dispatchers.Default) { calculateHardThings(30) }

    val sum = result1 + result2 + result3
    println("async/await result = $sum")

    val endTime = System.currentTimeMillis()
    println("Time taken: ${endTime - startTime}")
}

 

About the author 

Matt Rešetár

Matt is an app developer with a knack for teaching others. Working as a freelancer and most importantly developer educator, he is set on helping other people succeed in their Flutter app development career.

You may also like

  • Hi, I saw the tutorial. It is awesome. I already commented in youtube (arkorott) and recommended your tutorial on stack overflow.

    But I am still stuck. I realized that by doing this in the code below, I was creating a new thread but still blocking the main thread.

    fun getCrimes(): ArrayList = runBlocking(Dispatchers.Default) {
    val result = async { crimesDAO.getAllCrimes() }.await()
    return@runBlocking result as ArrayList
    }

    How do I have a coroutine run on a separate thread but get the result back on the main thread when finished?
    I asked this on Stack Overflow but have not yet solved it:

    https://stackoverflow.com/questions/52746866/android-room-kotlin-query-in-background-thread-return-value-problem/52752174#52752174

    Any help much appreciated !!
    Thanks

      • Not yet. Question.
        If for example I make a suspend function like this:

        suspend fun retrieveCrimes(): List {
        return crimesDAO.getAllCrimes() // This is the actual call to the DAO
        }

        How can I call it without blocking the main thread. What I tried (copied below) creates a new thread but blocks the calling one, so it defeats the purpose.

        fun getCrimes(): ArrayList = runBlocking(Dispatchers.Default) {
        val result = async { retrieveCrimes() }.await()
        return@runBlocking result as ArrayList
        }

        I still want to get the result but do not want to block the main thread

      • Not yet. Question.
        If for example I make a suspend function like this:

        suspend fun retrieveCrimes(): List {
        return crimesDAO.getAllCrimes() // This is the actual call to the DAO
        }

        How can I call it without blocking the main thread. What I tried (copied below) creates a new thread but blocks the calling one, so it defeats the purpose.

        I still want to get the result but do not want to block the main thread

  • First of all thanks for this great tutorial.

    I was playing with your code and noticed the followling:
    when changing the part of “// Complex calculation” to a “real” computational task the code in async-wait is not executed asynchronously anymore without setting a dispatcher like Dispatchers.Default.
    I changed delay(1000) to a do-while loop, which waits 1 second.

    var time = 0L
    do {
    time = System.currentTimeMillis() – start
    } while(time < 1000)

    Why is that?

  • I love the time you take to make these clear and friendly examples.
    Once I had a mentor that said that smart people usually do not know how to teach and the reason is that they see things as they were easy as it is for them… I am not saying you are not smart, I am sure you are incredibly smart, what I am saying is that you truly know how to teach, keep that great job please, the world needs more Matejs

    • Thank you very much, and thanks for learning here! I have a different thing in mind, it’s supposedly a quote by Einstein “If you can’t explain it simply, you don’t understand it well enough.”

  • I really feel happy that I finally understood coroutines basics after watching your tutorial video ! Thanks !

  • {"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}
    >