Understanding Kotlin’s coroutines

I am currently in the process of porting an Android app and I am therefore learning Kotlin at the same time, since this is now the recommended language for Android development. I have to say, I like this new language a lot : it reminds me of the conciseness and expressiveness of Python but with a robust typing system and bits of functional programming mixed in. Lots of great ideas (a lot of them inspired from a wide range of other languages) made their way into the language, such as :

Example of a one-liner possible in Kotlin :

// If there are 10 users or less that are in their 20s, print their names
println(users.filter { it.age in 20..30 }
             .takeIf { it.size <= 10 }
             ?.map { it.name }
             ?.joinToString() ?: "Too many users")

All of this, in addition to recent Android features such as LiveData and ViewBinding (think Observer pattern), makes the new implementation way shorter, clearer and more robust than the old Java one.

However, there is one other major feature offered by Kotlin that I had trouble wrapping my mind around : coroutines. Shortly speaking, coroutines are an interesting way to help you write safe, efficient and easy-to-read concurrent or asynchronous code. While powerful, this feature can be difficult to grasp at first when you are only used to the classic multi-threading coding techniques.

The official documentation goes into great lengths to explain what coroutines do and where they can prove useful, but is short on details about how they do it. I understand that some (most?) people don’t need more to get started, but for my down-to-earth mind, the examples seemed a bit “magical” at times and that, for me, made things paradoxically harder to understand.

This humble post is therefore an attempt at explaining coroutines from another, bottom-up point of view, hopefully helping similar-minded people to me to comprehend what is going on behind the curtain of coroutines, why they are useful, and what are their limitations. In a way, it aims at being the guide I would have wanted to have when I started learning them. It does not try, however, to be exhaustive : I will assume that you have already read (or intend to read) the official documentation (at least the first chapters).

A side note : as I said, I only recently started learning Kotlin, and I am nowhere near what you would call an expert. Everything explained below is to be taken with a grain of salt and is mainly destined to offer another perspective on the subject. The official documentation is obviously the definitive source of information, and if you spot a mistake, please report it in the comments below. Thanks!

  1. A quick reminder on processes, threads, and CPUs
    • Sequential execution
    • Multi-process and scheduler
    • Threads
    • Cores
  2. Different approaches to multitasking
    • Concurrent execution and its share of problems
    • Single-thread is not great either
    • An interesting alternative
  3. An internal, cooperative software scheduler
    • Coroutines are not concurrent
    • Suspending functions
    • Dispatchers and cooperative scheduling
    • Asynchronous execution
    • Cancellable suspending functions
    • Coroutine scopes and structured concurrency
    • Coroutine context
    • Starting a coroutine
  4. Examples
    • Example 1 : my first coroutine
    • Example 2 : non-concurrency
    • Example 3 : yielding
    • Example 4 : multi-threading
    • Example 5 : context switching
    • Example 6 : sub-scope
A sneak peak from the examples 😉

Let’s get started !

2 thoughts on “Understanding Kotlin’s coroutines

  1. Great article. I love that your examples also cover blocking calls like Thread.sleep and how specifying the dispatcher helps in this case. This would have helped me a lot when I first started working with coroutines!

Leave a Reply

Your email address will not be published. Required fields are marked *