Coroutines in Android: Simple & Easy way to doing Background task
By: Deepak Singh Gahlot
Tags: Android App Development, Android, AndroidDev, Suspend Coroutine, Kotlin Coroutines

Coroutines in Android: Simple & Easy way to doing Background task

Coroutines are basically lightweight threads, they are used for doing background work.

We have a lot of different ways to do a non-blocking UI operation(working on a different thread), to name a few “Creating multiple threads(using ExecutorService), using handlers, using Rx Java, using Async Tasks(don’t use it)”

So with all the available ways to do background work, Why should we use Coroutines?

When writing code that involves database transactions, network access, or any other expensive interaction, you might want to write async code and just provide a callback that gets executed after the expensive function is done. This isn’t a completely terrible idea because your code would run asynchronously and the callbacks would get executed on the main thread (as it should be). Right? On second thought, it’s a terrible idea because there is a high chance that there will be memory leaks with this approach. To prevent the OutOfMemoryError from happening, we would need to cancel all transactions when the activity is destroyed (which is cool until you need to handle a lot of things concurrently). In addition, callbacks also don’t allow the use of some features (e.g., exceptions).

Look at the code example below

fun getPersonDetails(){

makeApiCall { res ->

writeToDb(res) { rows ->

// ...

} } }

As stated earlier, we are trying to avoid memory leaks and coroutines help with that by allowing us to write sequential code. See below:

suspend fun getPersonDetails(){ val res = makeApiCall() writeToDb(res) } ... suspend fun makeApiCall(){ /* Code here */ }

Sequential code? Wouldn’t this block the main thread?

When using coroutines, that is not the case, and here’s why. The suspend keyword on the coroutine function makes all the difference. With coroutines, the makeApiCall() function is called (on the main thread). However, similar to the callback, the main thread isn’t blocked and makeApiCall() still runs on a different thread. When the response from makeApiCall() has been received, the coroutine is resumed. Basically, the coroutines library handles all these things for us without the need to create multiple callbacks. If you ask me, this makes the code much easier to read.

How coroutines work

Coroutines work with the suspend and resume mechanism. When a suspend function is called, Kotlin needs to keep track of the fact that it’s running a coroutine as opposed to a regular function. All of this happens on the call stack. When getDetails() is reached, it is called like a normal function and begins execution until it finds another suspend function ( makeApiCall() ). When this happens, the compiler transforms the suspending functions into a state machine.

When all the coroutines on the main thread are suspended, the main thread is free to do all other work so that our users can still successfully interact with our app while things are going on in the background. Once the result from a suspended function is ready, the function is resumed. For this to happen, it takes the saved state back to the stack and resumes the function.

What does this Lightweight Thread mean for Coroutines.?

Coroutines are not threads, you can think of a Coroutine as a task that is supposed to be done and a thread will be responsible to do that task. We can have a number of different Coroutines(Tasks) which will be executed on a single thread simultaneously

In using coroutines, we would be sure to never block the main thread when writing our code so that our apps won’t freeze or create a not so good experience for our users.

So I will be covering two common problems when coming across the background processing in Android and see how coroutines make them very easy as compared to other approaches in Android

1- How to communicate between the main thread and the background thread 2- If we have to make multiple background requests which depends on previous subsequent request results.

Suspend function -> A Suspending function is just a regular function with an additional suspend modifier which indicates that the function can suspend the execution of a coroutine. Suspending functions can invoke any other regular functions, but to actually suspend the execution, it has to be another suspending function. On the other hand, a suspending function cannot be invoked from a regular function, therefore several so-called coroutine builders are provided, which allow calling a suspending function from a regular non-suspending scope.

Also, we need to learn how to start a coroutine in the code. There are mainly two functions in Kotlin to start the coroutines.

launch{ }

async{ }

Launch Function

The launch will not block the main thread, but on the other hand, the execution of the rest part of the code will not wait for the launch result since the launch is not a suspended call. Following is the program using the Launch function.

// Kotlin Program For better understanding of launch

fun DemoLaunch()

{

var resultOne = "Android"

var resultTwo = "Kotlin"

Log.i("Launch", "Before")

launch(Dispatchers.IO) { resultOne = function1() }

launch(Dispatchers.IO) { resultTwo = function2() }

Log.i("Launch", "After")

val resultText = resultOne + resultTwo

Log.i("Launch", resultText)

} suspend fun function1(): String

{

delay(1000L)

val message = "function1"

Log.i("Launch", message)

return message

} suspend fun function2(): String

{

delay(100L)

val message = "function2"

Log.i("Launch", message)

return message

}

When to Use Launch?

Launch can be used at places where users do not want to use the returned result, which is later used in performing some other work. For example, It can be used at places involving tasks like update or changing a color, as in this case returned information would be of no use.

Async Function

Async is also used to start the coroutines, but it blocks the main thread at the entry point of the await() function in the program. Following is a Kotlin Program Using Async:

// kotlin program for demonstration of async fun DemoAsyncFunction { Log.i("Async", "Before") val resultOne = Async(Dispatchers.IO) { function1() } val resultTwo = Async(Dispatchers.IO) { function2() } Log.i("Async", "After") val resultText = resultOne.await() + resultTwo.await() Log.i("Async", resultText) } suspend fun function1(): String { delay(1000L) val message = "function1" Log.i("Async", message) return message } suspend fun function2(): String { delay(100L) val message = "function2" Log.i("Async", message) return message }

Since It is known that async is used to get the result back, & should be used only when we need the parallel execution, whereas the launch is used when we do not want to get the result back and is used for the operation such as updating of data, etc. As we know that async is the only way till now to start the coroutine and get the result back, but the problem with async arises when we do not want to make parallel network calls. It is known when async is used, one needs to use the await() function, which leads to blocking of the main thread, but here comes the concept of withContext which removes the problem of blocking the main thread.

withContext is nothing but another way of writing the async where one does not have to write await(). When withContext, is used, it runs the tasks in series instead of parallel. So one should remember that when we have a single task in the background and want to get back the result of that task, we should use withContext.

We will see all of it in action when we go through the code of your sample application

First, let’s add the dependencies to our Android project from the below link

https://developer.android.com/kotlin/coroutines

We have a very simple UI layout with a button, an image view with a text view as a heading.

Once the user will click on the button, we will make a dummy call which will simulate our network call. Below is the code for the main activity

package com.example.coroutinessample



import androidx.appcompat.app.AppCompatActivity

import android.os.Bundle

import com.bumptech.glide.Glide

import kotlinx.android.synthetic.main.activity_main.*

import kotlinx.coroutines.CoroutineScope

import kotlinx.coroutines.Dispatchers.IO

import kotlinx.coroutines.Dispatchers.Main

import kotlinx.coroutines.delay

import kotlinx.coroutines.launch

import kotlinx.coroutines.withContext



class MainActivity : AppCompatActivity() {



private val DUMMY_DATA = "Setting image 1"

private val DUMMY_DATA2 = "Setting image 2"



override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_main)



button.setOnClickListener {

CoroutineScope(IO).launch {

fakeDummyCall()

}

}

}



private suspend fun setImageOnMainThread(imageUrl: String) {

withContext(Main) {

Glide.with(applicationContext).load(imageUrl).into(imageview)

}

}



private suspend fun setTextOnMainThread(input: String) {

withContext(Main) {

textview.text = input

}

}



private suspend fun fakeDummyCall() {

val result1 = getResultDummyCall()

println("debug: $result1")

setTextOnMainThread(result1)

delay(1000)

setImageOnMainThread("https://homepages.cae.wisc.edu/~ece533/images/mountain.png")



val result2 = getResultDummyCall1()

println("debug: $result2")

setTextOnMainThread(result2)

delay(2000)

setImageOnMainThread("https://homepages.cae.wisc.edu/~ece533/images/airplane.png")

}



//delay will only delay this single coroutine not the entire thread

private suspend fun getResultDummyCall(): String {

delay(1000)

return DUMMY_DATA

}



//Just another dummy method to make another background thask

private suspend fun getResultDummyCall1(): String {

delay(2000)

return DUMMY_DATA2

}

}

Demo of the sample application

We can consider one more user case which is very common. Imagine we make a network call and due to some reason, we do not get a response back from the server for a very long time. If we want to handle this scenario using other different ways when compared to Coroutines. But when using coroutines this becomes very easy.

In the above application code, we added one more button, which is demoing this feature. We are using a method called withTimeoutOrNull(), and this takes a Long as a parameter which is the timeout in a millisecond.