A first look at Kotlin’s co-routines on Android

Co-routines have been the biggest addition in Kotlin 1.1. They are absolutely great because of their power, and the community is still discovering how to make the most of them.

Simply stated, co-routines are a way of writing asynchronous code sequentially. Instead of filling it all up with callbacks, you can write your lines of code one after the other. Some of them will have the ability to suspend execution and wait until the result is available.

Recommended Reading

If you have been trained as a C # developer, async / wait is the most similar concept. But co-routines in Kotlin are more powerful, because instead of being a specific implementation of the idea, they are a feature of language that can be implemented in different ways to solve different problems.

Co-routines are based on the idea of ​​suspend functions: functions that can stop execution when they are called and resume it once they have finished executing their own task

You can write your own implementation, or use one of the many options the Kotlin team and other independent developers have built.

You need to understand that co-routines are an experimental feature in Kotlin 1.1. This means that the implementation could change in the future, and although the old one will still be compatible, you might want to migrate to the new definition. As we will see later, you need to opt for this feature, otherwise you will see a warning when you use it.

This means that you should take this article as an example of what you can do, not as a general rule. Things can change a lot in the coming months.

Understanding how co-routines work

My goal with this article is to be able to take some basic concepts and put them into practice using the existing libraries, not to build your own implementations. But I think it’s important to understand something of how they work inside so you do not just blindly use what the bookstores provide.

The co-routines are based on the idea of suspending functions: functions that can stop execution when they are called and resume it once they have finished executing their own task.

Suspend functions are indicated by the reserved word suspend, and can only be called within other suspend functions or a co-routine.

This means that you can not call a sleep function anywhere. It is necessary that there is a surrounding function that constructs the co-routine and provides the required context for it to be done. Something like this:

fun  async(block: suspend () -> T)

I will not explain how to implement the above function. It is a complex process that goes beyond the scope of this article, and as I said there are already implemented solutions that will cover most of the cases.

If you are really interested in building yours, you can read the specification written in the Github of co-routines. You just need to know that the function can have any name you want to give it, and that it will have at least one block of suspension as a parameter.

Then you would implement a suspend function and call it within that block:

suspend fun mySuspendingFun(x: Int) : Result {
    ...
}

async { 
    val res = mySuspendingFun(20)
    print(res)
}

So co-routines are threads? Not quite. They work in a similar way, but they are much lighter and more efficient. You can have millions of co-routines running on a few threads, opening up a world of possibilities.

You can use co-routines in three ways:

  • Deployment from scratch : this involves building your own way of using co-routines. It is quite complicated and usually is not necessary at all.
  • Low level implementations : Kotlin offers a set of libraries that you can find in the kotlinx.coroutines repository, which solve some of the most difficult parts and offer a specific implementation for different scenarios. There is one for Android , for example.
  • Top-level implementations : If you are just looking for a solution that provides everything you need to start using co-routines, there are many bookstores that do all the dirty work for you, and the list keeps growing. I’m going to stick with Anko , which provides a solution that works great with Android, and that will probably be familiar.

Use Anko for co-routines

Since version 0.10, Anko has provided a couple of ways to use co-routines on Android.

The first one is very similar to what we have seen in the example above, and it is also similar to what other libraries do.

First, you need to create an asynchronous block where the suspend functions can be called:

async(UI) {
    ...
}

The UI argument is the execution context for the block async.

Then you can create blocks that are executed in a thread in the background and return the result to the thread of the UI. These blocks are defined using the function bg:

async(UI) {
    val r1: Deferred = bg { fetchResult1() }
    val r2: Deferred = bg { fetchResult2() }
    updateUI(r1.await(), r2.await())
}

bgReturns an object Deferred, which will suspend the co-routine when the function await()is called , only until the result is returned. We will use this solution in the example below.

As you may know, one of the most interesting features when you are learning Kotlin is that the compiler is able to infer the type of variables, so this can be made easier:

async(UI) {
    val r1 = bg { fetchResult1() }
    val r2 = bg { fetchResult2() }
    updateUI(r1.await(), r2.await())
}

The second alternative is to make use of the integration with the listeners that provide specific sub-libraries, depending on the listener that you are going to use.

For example, in anko-sdk15-coroutines, there is a onClicklistener whose lambda is in fact a co-routine. So you can start using suspend functions immediately inside the listener block.

textView.onClick {
    val r1 = bg { fetchResult1() }
    val r2 = bg { fetchResult2() }
    updateUI(r1.await(), r2.await())
}

As you can see the result is quite similar to the previous one. You’re just saving yourself some code.

To use it, you’ll need to add some of these dependencies, depending on the listener you want to use:

compile "org.jetbrains.anko:anko-sdk15-coroutines:$anko_version"
compile "org.jetbrains.anko:anko-appcompat-v7-coroutines:$anko_version"
compile "org.jetbrains.anko:anko-design-coroutines:$anko_version"

Using co-routines in an example

In the example that was developed in this book (which you can find here in Github ), a simple application of time is created.

To use Anko’s co-routines, we first need to include the new dependency:

compile "org.jetbrains.anko:anko-coroutines:$anko_version"

Then, if you remember, I told you that you needed to activate this feature, otherwise it will display a warning. To do this, simply add this line to the file gradle.propertiesin the root directory (create it if it does not already exist):

kotlin.coroutines=enable

Now, you have everything you need to start using co-routines. Let’s go first to the detail activity. It only calls the database (which is responsible for storing the weekly forecast) using a specific command.

Here is the resulting code:

async(UI) {
    val id = intent.getLongExtra(ID, -1)
    val result = bg { RequestDayForecastCommand(id).execute() }
    bindForecast(result.await())
}

It’s great! The forecast of the time is requested in a thread in the background thanks to the function bg, which returns a deferred result. Waiting for this result suspends the co-routine by calling theawait, until the forecast is ready to be returned.

But not everything is so beautiful. What’s going on here? Co-routines have a problem: they are keeping a reference to DetailActivity, causing a leak if the request never ends for example.

Do not worry, since Anko has a solution. You can create a weak reference to your activity, and use it instead:

val ref = asReference()
val id = intent.getLongExtra(ID, -1)
async(UI) {
    val result = bg { RequestDayForecastCommand(id).execute() }
    ref().bindForecast(result.await())
}

This reference will allow calling the activity when it is available, and will cancel the co-routine in case the activity has been eliminated. Make sure that all calls to methods of the activity or properties are made through this objectref .

But this can be a little complicated if the co-routine interacts several times with the activity. In MainActivity, for example, using this solution is going to be a little more complicated.

This activity will call an endpoint to request a weekly forecast based on a zipCode:

private fun loadForecast() {
    val ref = asReference()
    val localZipCode = zipCode
    async(UI) {
        val result = bg { RequestForecastCommand(localZipCode).execute() }
        val weekForecast = result.await()
        ref().updateUI(weekForecast)
    }
}

You can not use ref()within the block bg, because the code inside that block is not a suspend context, so you need to save it zipCodeto another local variable.

Personally I think provoking a leak of activity for 1-2 seconds is not so bad, and probably will not be worth the boilerplate. So if you can make sure that your background process can not last forever (for example, setting a maximum wait time on your server requests), you’ll be safe from not usingasReference() .

In this way, the changes MainActivitywould be simpler:

private fun loadForecast() = async(UI) {
    val result = bg { RequestForecastCommand(zipCode).execute() }
    updateUI(result.await())
}

So with all this, you now have your asynchronous code written synchronously easily.

This code is quite simple, but imagine other complex situations in which the result of one operation in the background is used by the next one, or when you need to iterate over a list and execute a request for each element.

All this can be written as normal synchronous code, which will be much easier to read and maintain.

For simplicity, co-routines are a way to write asynchronous code sequentially

There is still much more to learning to get the most out of co-routines. So if you have some more experience on this, use the comments to tell us more about it.

Reference : antonioleiva

Written by
Am a tech geek.. Do you wanna know more about me..? My contents will do tell you.

Have your say!

0 0

2 Comments

  1. I visited lots of web site but I conceive this one holds something special in it in it

    • Hey thanks for your kind word

Comments are now closed for this post.

Lost Password

Please enter your username or email address. You will receive a link to create a new password via email.