USA
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!
Roman is a Senior Software Developer at Sigma Software. He is an experienced Android Developer with a demonstrated history of working in the computer software industry. He made his acquaintance with Android platform in 2009 and has been developing mobile applications on a constant basis since 2012. Recently he got to know Kotlin language (after Google announced it’s official support) and now is eager to share his knowledge with others.
Organ transplantation is a process that allows patients with terminal organ diseases to get a new opportunity for life. However, this critical field is plagued ...
Organ transplantation is one of the biggest achievements in modern medicine, giving patients with organ failure a second chance at life. Every transplant relies...
Non-structured data is gradually assuming a critical role in analytics across the healthcare industry, encompassing an assortment of forms such as textual (note...