Android Coroutines with Kotlin: getting rid of runOnUiThread and Callbacks; cleaner thread handling; and more

With the release of Kotlin 1.1.0, I decided to give a try to Kotlin’s Coroutines on Android. I had some ideas about what I wanted to build as a Proof of Concept, trying to showcase what I think are the most obvious advantages of using coroutines.

Update March 1st: Updated and tested with Kotlin 1.1.0 and coroutines-core 0.12

Update Feb 24th: Tested with Kotlin RC 1.1 and updated the repo. Check the announcement here.

Requirements

  • Call a service using okHttp and parse/display the information without using a callback
  • Handle the exception if the device is not connected to the internet (IOException) without using a callback
  • Display the information coming from the server without using runOnUIThread
  • Use a minSdkVersion of 16

Show me the code

All the code I will talk about is here. In order to build it you just need to install Kotlin 1.1.0 in a recent version of Android Studio.

Go to Tools → Kotlin → Configure Kotlin Plugin Updates, then select
“Stable” in the Update channel drop-down list, then
press Check for updates, install it, and restart Android Studio

Intro to Coroutines

In a nutshell, a coroutine is an algorithm that has the capability of suspending and resuming its execution, being able to execute parts of itself in different threads. It’s important to mention that suspending the execution of a coroutine will not block the thread in which it’s being executed. Let’s use pseudo code for an example:

1 start coroutine
2 var rawData = fetchDataFromServer().await()
3 var data = parseData(rawData).await()
4 displayInList(data)

Lines 2, and 3 will cause our algorithm to be suspended until the functions we are calling finish their execution (those functions will be executed in different threads.) Once that is done, our algorithm will resume where it was, but now giving us access to the result the operations made in other threads.

Things to notice:

  • The thread we are using is not going to be blocked while the algorithm is suspended. If we execute this coroutine in Android, in the UI thread, the app is not going to freeze while waiting for the info to be fetched or parsed
  • Lines 2 and 3 are calling functions that will be executed in other threads. This means that our coroutine is allowing us to write multithreaded code in a simple and natural way
  • If any of the lines in our algorithm throws an exception, the propagation will be done to the thread where our coroutine is. We can, then, catch the expected exceptions as if everything was synchronous

With this in mind, let’s start building an app with coroutines.

Calling the service without using a Callback

The first requirement is to call the service without using a callback. That part is easy, as okHttp offers synchronous execution of a Request. SampleClient.kt has the code for that.

1 val client = OkHttpClient()
...
2 val request = Request.Builder().url("https://jsonplaceholder.typicode.com/posts").build()
3 val response = client.newCall(request).execute()
4 val postsType = object : TypeToken<List<Post>>() {}.type
5 Gson().fromJson<List<Post>>(response.body().string(), postsType)

Nothing particular in there, right? except that if you look in the repo you will see that the code from lines 2 to 5 is inside a call to async(CommonPool), and the method is returning a Deferred<List<Post>>.

1 fun fetchPosts() : Deferred<List<Post>> {
2 return async(CommonPool) {
3 // Call the service and parse the response
4 }
5 }

async(context) - Creates new coroutine and returns its future result as an implementation of [Deferred].

So, basically, we are wrapping our call to the service into a Deferred coroutine, and returning the parsed list of posts at the end. Notice that async will return a Deferred of whichever its last expression is. That’s the reason why in the first example of code we only parse the data but don’t mention explicitly that it’s what we want to return. Another example:

val job = async(CommonPool) {
true
}

In this example job will be a Deferred<Boolean>.

Handle IOException if the device is not connected (without a Callback)

The purpose of this requirement is to show that we can handle exceptions that happen in other threads in a clean way when using coroutines. MainActivity.kt has the code for it.

1 try {
2 val result = SampleClient.fetchPosts()
3 postsAdapter.setElements(result.await())
4
postsAdapter.notifyDataSetChanged()
5 } catch (exception: IOException){
6 Toast.makeText(this@MainActivity, "Phone not connected or service down", Toast.LENGTH_SHORT).show()
7 }

Line 2 will obtain the Deferred<List<Post>> that we saw before, and the request will be executed in another thread as soon as created (it’s the default behavior for async.) If the device is not connected to the internet, okHttp’s execution of the Request will throw an IOException that will be caught in line 5 and will then display a toast with a dummy message.

The big part of this is that we don’t need callbacks to handle exceptions in our coroutines. This will be excellent when we need to call multiple services in parallel or sequentially, since we wouldn’t need complex-to-debug Callback Hell, we can just have them executing together in a coroutine and gracefully handle the exceptions in one block.

Display the result of the Request without using runOnUIThread

Android requires the UI to be updated on the UI Thread, which is the Main Thread of an app. This, with plain Android (meaning no Rx or other libs) means that we often have to use runOnUiThread after we realize an operation in a background thread that we want to reflect in the UI.

With coroutines we can have a context that guarantees that by default everything is executed in the UI thread, and then we can have some parts of the coroutine to be executed in a background thread. Let’s take another look at MainActivity.kt.

1 launch(Android) {
2 // try
3 val result = SampleClient.fetchPosts()
4 postsAdapter.setElements(result.await())
5 postsAdapter.notifyDataSetChanged()
6 // catch
7 }

launch - Launches new coroutine without blocking current thread and returns a reference to the coroutine as a [Job].

Line 1 is launching a coroutine with the context Android, which is a simple implementation of CoroutineContext that I put here. It is a context that provides an AndroidContinuation, which is a continuation I made to always resume in the main thread. For example:

1 override fun resume(value: T) {
2 if (Looper.myLooper() == Looper.getMainLooper())
3 cont.resume(value)
4 else Handler(Looper.getMainLooper()).post {
5 cont.resume(value)
6 }
7 }

This means that any coroutine executed with that Android context will be resumed in the main thread. That’s the reason why updating and notifying the adapter didn’t require runOnUIThread. The same for displaying the toast if the exception happens.

If you go back and check the code to call the service, async is being executed with the context CommonPool, this context will execute and resume in background, in a shared pool of threads.

Suspending the coroutine until we have the information ready to be displayed

As we saw before, SampleClient.fetchPosts() will be executed in a background thread and will return a Deferred. In order to suspend the current coroutine until the fetching and processing of the information is completed, we call the method await of Deferred.

1 val result = SampleClient.fetchPosts()
2 postsAdapter.setElements(result.await())
3 postsAdapter.notifyDataSetChanged()

Line 1 will start fetching and parsing the info in background; line 2 will pause the execution of the coroutine until the information is ready, and then set the result to the adapter; and once that is done, line 3 will refresh the recycler view.

Conclusions

  • Coroutines is opening the door for developers to do multithreading in a way that is easily debuggable and maintainable
  • This is definitely something that every Kotlin developer wants to understand and keep present for their projects
  • The folks at jetbrains are no kidding when they say that they want to improve the way we write code. Cheers to that

Written by

Android Developer, Kotlin and Flutter evangelist, macastiblancot[at]gmail, https://www.linkedin.com/in/macastiblancot/

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store