Kotlin Coroutines vs. Java Threads vs. Android AsyncTasks

This year Kotlin was announced as officially supported language by Google. Of course, there’s a lot of information about that language and its benefits, some of us even tried it in their new projects.

In this article, I’d like not to focus on the language itself, but rather share some info and my personal impression about an interesting functionality available since Kotlin v.1.1, called coroutines. In general, this is a lightweight alternative to Java Threads and AsyncTasks in Android.

As far as I know, this is an experimental functionality that is not yet available out of the box. However, enabling it doesn’t take a lot of effort. Just simply add the following to your build.gradle file:

kotlin {

experimental {

coroutines ‘enable’

}

}

 

dependencies {

compile “org.jetbrains.kotlinx:kotlinx-coroutines-core:0.18”

}

 

repositories {

maven { url “https://kotlin.bintray.com/kotlinx/” }

}

 

Voila! Coroutines are now enabled! 🙂

Let’s proceed with the examples.

The simplest way of using coroutines is launch(CommonPool), where CommonPool is the reusable thread pool and uses java.util.concurrent.ForkJoinPool “under the hood”. Here is an example:

println(“Begin“)

launch(CommonPool){

Thread.sleep(500) // this is blocking example

println(“Do some async stuff…”)

}

println(“End“)

 

The output log will show the following:

Begin

Do some async stuff…

End

 

The example above was used for demonstration purposes only, so using such an approach in real life is not recommended. Let’s use a coroutine in a more efficient way. Here is another example (non-blocking):

println(“Begin“)

launch(CommonPool) {

for(i in  1..10) {

delay(100) //non-blocking example

}

println(“Do some async stuff…”)

}

println(“End“)

 

The output log in this case will be the following:

Begin

End

Do some async stuff…

 

As we can see now, delay() is similar to Thread.sleep(), but it doesn’t block the thread, only the coroutine from which it was called. Great news!

Now let’s do something more advanced with coroutines. The following example will demonstrate how async() and await() work “under the hood”. For demonstration purposes, let’s create a function that counts a sum of integers from 1 to the value specified as a parameter, i.e. sum(1) will return 1, sum(10) will return 55, sum(100) will return 5050, etc.:

fun sum(num : Int) : Deferred<Long> {

return async(CommonPool) {

var result:Long = 1

for (i in 2..num) {

result += i

delay(10) // just to check the execution time

}

result //return statement

}

}

 

And now let’s make 2 consecutive calls, process them and print the result:

println(“Begin“)

launch(CommonPool) {

val sum10 = sum(10)

val sum100 = sum(100)

println((sum10.await() + sum100.await()))

}

println(“End“)

 

The log will show the following:

Begin

End

5105

 

The interesting fact is that the result will appear in 1.0 second (i.e. 10 ms * 100), meaning that the println statement won’t be executed until all the results from coroutines are returned (de facto when the longest coroutine returns the result).

OK, if this is clear, let’s move to a more practical part, i.e. doing the async stuff and returning results to UI thread. For this purpose, we can use kotlinx-coroutines-android module that provides UI context for the coroutines (replacing the CommonPool). Just add the following to your gradle file:

dependencies {

compile “org.jetbrains.kotlinx:kotlinx-coroutines-android:0.18”

}

 

Taking into account the mentioned above, passing the result to UI thread looks like following:

println(“Begin“)

launch(UI) {

val sum10 = sum(10)

val sum100 = sum(100)

textView1.text = (sum10.await() + sum100.await()).toString()

}

println(“End“)

 

Looks like a replacement for AsyncTasks, isn’t it? In this case the following question could be expected: what happens when the context is destroyed (on screen rotation)? I haven’t noticed any issues in this case (like exceptions or memory leaks), everything works like a charm!

Here is a more practical example – fetch image, watermark and combine them:

launch(UI) {

try {

val image = ImageUtil.fetchImageByUrl(url1)

val watermark = ImageUtil.fetchImageByUrl(url2)

 

//this is async() function

val processedImage = combineImages(image.await(), watermark.await())

 

imageView.setImageBitmap(processedImage.await())

} catch (e : CustomException) {

//process the exception

}

}

 

Therefore, taking this into account, using AsyncTasks with Kotlin makes no sense, coroutines appear to be a very efficient replacement. Less code, more stability. Say goodbye to lots of callbacks, rotation issues, etc. Amazing! 🙂

In this article, I demonstrated only the most basic aspects of coroutines. There are lots of other features, just check the official reference: https://kotlinlang.org/docs/reference/coroutines.html.

Thanks for your attention and have a nice day!

Share article: