Achieving Async/Await in the Android Wasteland

Async Workflows with Kotlin Pt I

Sampson Oliver
7 min readNov 14, 2017

This post is part of a series in which I set out how to implement common async-handling patterns in Android and Java using Kotlin Coroutines. If you haven’t seen them, or just aren’t as up to date as you’d like, have a squiz:

I recently spent six weeks on a new project, building an ExpressJS app in TypeScript with es2017 transpilation and support for async/await. Then I came back to my Android project that was still — like most Android projects — mired in callback hell for all of its async code. Of course, Java 8 adds CompleteableFuture, but this is only helpful if you’re only targeting API level 24 and up, and is unhelpful in general if you’re stuck in Java 6 or 7 altogether.

So I set out to try and achieve async task execution, satisfying some minimum criteria:

  • No boilerplate, no overriding of class methods, no callback signatures
  • Easily compose-able syntax, with simple parallel- and sequential- chaining
  • Transparent thread use; no managing background and UI thread execution
  • Be Java 6 or 7 compatible, support Android API 14+, optionally use Kotlin

The goal was to write code that would look something (roughly) like this:

function doAsync() {
sleep(500)
return Promise.resolve(true)
}
async function runAsync() {
var result: Promise<boolean> = doAsync()
var resultSync: boolean = await doAsync()
}

I’d already adopted Kotlin in my Android project, and that turned out to be a necessary component. If you haven’t adopted Kotlin yet for a Java project, you should.

What Your Code Might Look Like: Part #1 — Loaders and AsyncTasks

These were obtuse, too tightly bound to activity lifecycle to be particularly flexible, and their pros just don’t outweigh their cons.

Here’s an example AsyncTask

class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
// do background work
}

protected void onProgressUpdate(Integer... progress) {
// optionally report progress
}

protected void onPostExecute(Long result) {
// do something on the UI thread
}
}
new DownloadFilesTask().execute(url1, url2, url3);

Concrete class implementation for every unit of asynchronous work breaks our developer flow and cohesion, and is inherently not compose-able.

Loaders behave the same, though with the added pro of tightly coupling with an Activity lifecycle. This is often good, but not very reusable.

What Your Code Might Look Like: Part #2 — Callbacks

Most http libraries for Android, like Volley or OkHttp, yield a callback interface to the developer. Failing this, many devs — myself included — baked their own callback system on top of the base Android classes.

I think everybody on Earth knows what a callback looks like, but here’s an example from OkHttp:

Request request = new Request(...)

client.newCall(request).enqueue(new Callback() {
@Override public void onFailure(Call call, IOException e) {
// Handle error
}

@Override public void onResponse(Call call, Response response) {
// Handle response
}
});

It’s better. Async services run atomic pieces of work, and can be executed anywhere. Compose-ability is up, boilerplate is down.

What Your Code Might Look Like Part #3 — Kotlin Syntactic Sugar

This is less historic and more ‘now’.

Kotlin lets us do callbacks as above, but prettier. For example, here’s an callback object using a builder-like syntax.

class Callback<T>(
private var handler: ((T) -> Unit)? = null,
private var errHandler: ((Exception) -> Unit)? = null
) {
fun onFail(handler: (Exception) -> Unit): Callback<T> {
this.errHandler = handler
return this
}

fun onSucceed(handler: (T) -> Unit): Callback<T> {
this.handler = handler
return this
}

fun resolve(result: T) = handler?.invoke(result)
fun reject(err: Exception) = errHandler?.invoke(err)
}

Then start invoking asynchronous functions that return Callback objects, like so:

fun doAsync(): Callback<Int> {
val callback = Callback<Int>()
AsyncWorker.doTaskAndNotifyCallbackOfResult(callback);
return callback
}
// ...doAsync()
.onSucceed { result -> /* doThings */ }
.onFail { err -> /* doThings */ }

Boilerplate way down, but they’re still callbacks. Compose-ability and chaining is still a bust.

What Your Code Could Look Like: Kotlin Coroutines

Kotlin 1.1 introduced coroutines on the JVM as an experimental feature, with optional bindings for Android. If you’re not too familiar with what a coroutine is, I don’t blame you. They’re a bit magical.

A coroutine is, basically, like a micro-thread. They define a block of work that can be carried out on a coroutine runner, and a coroutine runner is something smart enough to know how to schedule itself on and off of a pool of worker threads, in small enough slices that the threads aren’t blocked for long enough to cause a problem.

Coroutines work in conjunction with something called a suspending function, which is basically just a function that can be paused and resumed by the runner.

Here’s how you’d run a coroutine on Android

val hello: TextView = ...fun doWork {
launch(UI) {
for (i in 10 downTo 1) {
hello.text = "Countdown $i ..."
delay(500)
}
hello.text = "Done!"
}
}

Note that the really special thing here is that you can call doWork() from the UI thread of your app, and not only with that delay(500) not block your UI thread at all, but the updates to hello.text (which is a TextView) won’t complain about being run off of the UI thread. Magic!

So let’s make it happen for doing async work, starting by adding it to a project.

Adding coroutines is straightforward enough. If you’ve already added Kotlin to your project, then just add coroutines as a gradle dependency by adding to your build.gradle

compile "org.jetbrains.kotlinx:kotlinx-coroutines-android:0.19.3"

and then in your gradle.properties file, enable them with

kotlin.coroutines=enable

Now you’re ready to go.

Async/Await with Kotlin Coroutines

Taking the OkHttp code from earlier, it first needs to be adapted so that it’s able to be execute synchronously — because coroutines run synchronously. OkHttp handily provides a synchronous recipe, and other libraries like Volley can be made to take a RequestFuture which can then be waited on.

Here’s OkHttp running synchronously:

val request = new Request(...)try {
val response = client.newCall(request).execute()
if (!response.isSuccessful()) {
throw new IOException();
}
// do something
} catch (e: Exception) {
// do something
}

Normally, running this on the UI thread would of course be a blocking call that would stall all UI interactions. So just wrap it into a coroutine.

launch(UI) {
val request = new Request(...)
try {
val response = client.newCall(request).execute()
if (!response.isSuccessful()) {
throw new IOException();
}
updateViewWithResult(response)
} catch (e: Exception) {
updateViewWithErr(e)
}
}

Easy. Now it’ll run on the UI thread, but should be non-blocking. However, even though it’s non-blocking, it still pollutes the UI thread, potentially adding some lag or stutter, because the work is still happening within the UI thread pool. That’s not quite ideal. Instead, the updateView... calls needs to run on the UI, and the request needs to run on a worker pool.

launch(UI) {
val request = new Request(...)
try {
val deferred = async { client.newCall(request).execute() }
val response = deferred.await()
if (!response.isSuccessful()) {
throw new IOException();
}
updateViewWithResult(response)
} catch (e: Exception) {
updateViewWithErr(e)
}
}

Notice now how we start a coroutine bound to the UI context, but within it we start another async coroutine, which will be bound to the CommonPool. async returns a Deferred<T> which can be awaited, so the http work gets done in another pool, and the UI coroutine suspends until it’s complete.

Eaaaasy!

Optional Wrappers for Async/Await

You can definitely take the above and run with it. It’s as easy as that. A lot of other discussion and solutions on Kotlin Coroutines take what I feel is a heavy-handed approach with their own bindings. This is a matter of personal taste, and certainly doesn’t diminish such approaches — they’re really excellent! — I just prefer minimalism with my own definitions, and prefer to avoid dependencies where I can.

So here’s three lines of code I’ve found really helpful.

The first is a wrapper to just neaten up the entrypoint — in Android, all coroutine work is liable to be launched from the UI thread, so cleaning up that launch(UI){ … }syntax into something simpler, like coroutine { ... } is a win.

fun coroutine(runner: suspend CoroutineScope.() -> Unit) = launch(UI) { runner.invoke((this)) }

The next two wrappers are to introduce an async/await syntax more similar to what you’d see in JavaScript/es2017.

suspend fun <T> CoroutineScope.await(block: () -> Deferred<T>): T = block().await()
suspend fun <T> CoroutineScope.awaitAsync(block: () -> T): T = async { block() }.await()

This just defines extension methods on CoroutineScope, which is the scope provided within launch (or coroutine). Now within those scopes, you can await a function block that yields a Deferred<T>, or awaitAsync on any synchronous block, so working with Deferred<T>’s can be skipped. Note the use of suspend which means this function is able to be suspended by a coroutine runner, and hence is permitted to call await.

Now we write code that looks more like this

fun doAsyncWork() = async { /* doSomethingAndReturnResult */ }
fun doSyncWork() = { /* doSomethingAndReturnResult */
fun doSuspendingWorkOnUiThread() = coroutine {
try {
val result = await { doAsyncWork() }
val otherResult = awaitAsync { doSyncWork
handleResultOnUiThread(result, otherResult)
} catch (e: Exception) {
handleErrorOnUiThread(e)
}
}

Which is semantically more alike to what you would see in es2017, where you would await an async function at the call-site.

--

--

Sampson Oliver

Team lead @acloudguru, serverless junky, runner, feminist, & infrequent music producer. @sampsonjoliver on GitHub/twitter et al