Demystifying Kotlin Coroutines

Jitin Sharma
ProAndroidDev
Published in
4 min readAug 5, 2018

--

Photo by Artem Kovalev on Unsplash

Kotlin coroutines offer a new way of asynchronous executions but directly looking at library functions may become a bit confusing.

In this article, I’ll try to demystify coroutines.

Let’s start at basic. We have a something called launch which can be used to launch a coroutine

private fun myHeavyFunction() {
Log.e("Thread Running ", Thread.currentThread().name)
}
val job = launch { myHeavyFunction() }

The above snippet represents a very simple way for execute a function asynchronously using launch which returns a Job. Job represents a coroutine job which can be cancelled or queried for it’s status.

override fun onStop() {
if (job.isActive) {
job.cancel()
}
}

Now if we look at our logs to check at which thread our function was actually running, we get something like this

E/Thread Running: ForkJoinPool.commonPool-worker-2

Our code was running inside a thread where did that come from. Let’s dig a little bit into declaration of launch itself.

Since Coroutines are multi-platform, we have actual variable for JVM dispatcher

launch takes CoroutineContext as first parameter which then defaults to DefaultDispatcher which is a CommonPool class which creates a CoroutineContext with Executors based on total number of processors in device. Full source here

Coroutine Builders like launch can accept a CoroutineDispatcher which is actually responsible for running code in separate thread.

We can create our own dispatchers easily.

val singleThreadDispatcher = newSingleThreadContext("singleThreadDispatcher")

newSingleThreadContext is provided by Kotlin coroutines library itself to create a context which will run on just single thread. We can create our own function on top of this

fun <T> singleThreadAsync(block: () -> T): Job = launch(singleThreadDispatcher) { block.invoke() }job = singleThreadAsync { myHeavyFunction() }

And here’s the log

E/Thread Running: singleThreadDispatcher

So we just created our own simple coroutine with our own threading scheme :)

Let’s see what we can do more with Dispatchers

Here we have created three different dispatchers and using override method dispatch we are executing a Runnable block in a different way in each one of them -> a simple thread, using RxJava and on Android main thread using Handler.

If we execute our function using these dispatchers, we get these logs

E/Thread Running: Thread-582
E/Thread Running: RxCachedThreadScheduler-1
E/Thread Running: main

This really shows the power of coroutines because Coroutines in the end are just language syntax, they have nothing to do with platform which they are running on. That responsibility is delegated to the developer himself using a set of functions, he may execute coroutines as he like on a Rx thread or on the main thread.

Coroutines are like empty ice-cream cones, it’s upto you which ice-cream you want to fill in them

Thread-less Asynchronism

Writing asynchronous code is traditionally considered a job for threads which is not always true. Let’s see how we can tackle this using Coroutines

Let’s consider a sequence of function executions

mySmallFunction1() 
myHeavyFunction() // Takes 3 seconds to execute
mySmallFunction2()
//Order
E/mySmallFunction1 running on: main
E/myHeavyFunction running on: main
E/mySmallFunction2 running on: main

Now since myHeavyFunction() takes long to execute, we may want to execute it asynchronously.

mySmallFunction1()
thread { myHeavyFunction() } //Execution in a separate thread.
mySmallFunction2()
//Order
E/mySmallFunction1 running on: main
E/mySmallFunction2 running on: main
E/myHeavyFunction running on: Thread-697

Here we moved myHeavyFunction() to a separate thread and executed it asynchronously. But what if

mySmallFunction1()
launch(UI) { myHeavyFunction() }
mySmallFunction2()
//Order
E/mySmallFunction1 running on: main
E/mySmallFunction2 running on: main
E/myHeavyFunction running on: main

Here we executed the heavy function in a Coroutine context(UI: provided by coroutine-android library) which is running on main thread. The execution is still asynchronous since Coroutines handle that part through suspend functions but execution still happened on main thread without creating an extra thread.

Practically Coroutines

In most of the practical we would require a callback from asynchronous execution, so that we can update UI etc. that where Deferred comes in

Deferred itself extends Job but with an additional feature where it can return future values once a function has completed execution.

Let’s see what we did here

  1. launch(UI) creates a coroutine Job with Android’s UI thread context.
  2. We invoke our function inside another coroutine created via async. The only difference is this coroutine returns a Deferred value. async is part of coroutine library.
  3. We call await() function to capture future value of Deferred inside the second lambda parameter. This is captured inside UI context of the first coroutine.

All in all, we have created an async executor where we can pass functions and have them execute asynchronously and then return values back to UI thread.

Now where can we use this -> Room DB Queries :)

We can do an insert to DB with singleThreadAsync as an fire and forget request.

Whereas when we are retrieving data from DB, we can use our asyncExecutor to retrieve a list of objects and then apply all the kotlin goodness using operators from Collection Framework!

Hope you found this article useful!

You can reach me at twitter here.

Thanks for reading!

--

--