VGTech is a blog where the developers and devops of Norways most visited website share code and tricks of the trade… Read more



Are you brilliant? We're hiring. Read more

Async patterns on Android: Kotlin with coroutines

Android

9 months ago I wrote about asynchronous programming patters in different languages covering C#, Javascript and Java. Lately I’ve been digging into Kotlin and specifically the Coroutines implementation in their 1.1 beta. This finally brings the async/await-pattern to Android – a pattern I grew learned to love when it was introduced to C# some years ago. The main benefit from my point of view is how it integrates asynchronous programming into the language syntax, making writing, and even more important: reading asynchronous code simple.

What are coroutines?

Although I was familiar with async/await, the term “coroutines” was new to me. I’m going to quote the author of Kotlin Coroutines on the defnition:

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 and has a similar life-cycle — it is created and started, but it is not bound to any particular thread. It may suspend its execution in one thread and resume in another one. Moreover, like a future or promise, it may complete with some result or exception.

So from a programmer’s point of view: it’s just a stateful block of code. It’s the contract of where and when it executes which makes it a coroutine. It is not a thread but it does indeed execute on a thread (all code does). You as a developer can control where it is to execute by specifying a Scheduler. In Kotlin on Android the most common use case is to schedule it on the CommonPool thread pool, which is a pool in the size order of 3-4 background threads.

Kotlin does not reinvent the wheel on Scheduling and Thread Pooling – under the hood of CommonPool, the JVM ForkJoinPool.commonPool() is used if present. If not then a fallback solution is provided by the Kotlin coroutine implementation.

The base interface for a Coroutine instance in Kotlin is a Job. If you want more control over the life cycle you can create a Deferred using the async()-method which gives the Job five states instead of three:

  • New
  • Active
  • Resolved
  • Failed*
  • Cancelled*

*) Only present for Deferred. Instead of Resolved you have Completed for the base interface Job.

The async patterns

As initially stated I wanted to see how the three use cases with serial, parallel fork-join and parallel take-first execution manifested itself in Kotlin with coroutines. The code speaks for itself. The serial and parallel-join cases were trivial. For the Parallell-take-first implementation I couldn’t find a helper method to do what I wanted; wait until the first of X number of jobs have finished and use that result. This is equivalent to RxJava’s .first() and C#’s Task.WaitAll. To accomplish this I implemented a helper method awaitFirst. This implementation may or may not be sound – please correct me if it’s bad.

See my original post for the explanation of the use cases

Support functions

Show code
suspend fun getSlowStringJob(s: String) : String {
    delay(250)
    return s
}

suspend fun getSlowIntJob(i: Int) : Int {
    delay(250)
    return i
}

// Returns the deferred job which completes first
suspend fun  awaitFirst(jobs : Array<Deferred>) : Deferred {
    var doneJob : Deferred? = null
    while (doneJob == null) {
        for (job in jobs) {
            if (job.isCompleted) {
                doneJob = job
                break;
            }
            yield()
        }
    }
    return doneJob
}

Pipeline / Serial execution

Show code
launch(CommonPool) {
    val res1 = getSlowStringJob("Hello")
    val res2 = getSlowIntJob(res1.length)
    val res3 = getSlowStringJob("First string length: " + res2)
    System.out.println(res3)
}

Parallel fork-join

Show code
val job1 = async(CommonPool) { getSlowIntJob(1) }
val job2 = async(CommonPool) { getSlowIntJob(2) }
val job3 = async(CommonPool) { getSlowIntJob(3) }
launch(CommonPool) {
    val sum = job1.await() + job2.await() + job3.await()
    System.out.println("Sum: " + sum)
}

Parallel take first

Show code
val job1 = async(CommonPool) { getSlowIntJob(1) }
val job2 = async(CommonPool) { getSlowIntJob(2) }
val job3 = async(CommonPool) { getSlowIntJob(3) }
launch(CommonPool) {
    val jobs = arrayOf(job1, job2, job3)
    val first = awaitFirst(jobs)
    System.out.println("Finished first: " + first.getCompleted())
}

Summary

Since we’re already using RxAndroid in our projects at VG, taking the step to Kotlin Coroutines is mainly an improvement of syntax. If you’re still doing callbacks though, this step is huge. It basically makes asynchronous code look synchronous. Make no mistake though – it still involves the complexity of threads and concurrency. You need to know what you’re doing and what’s happening under the hood to avoid bugs. But once you do, this will bring your code to the next level and can have a positive impact on both performance and reliability. I dare say Kotlin Coroutines are much simpler to work with than RxAndroid/RxJava, so I hope that in the future we’ll see support libraries (HTTP comms, UI) based on Coroutines that make the dependancy to RxJava less or non-existant.

Android developer at VG


0 comments

    Leave your comment