Introduction
With Android 11 on the way, the Android Dev Team has decided to deprecate the AsyncTask class. An interesting move, but expected, especially since a lot of developers have been largely avoiding using AsyncTasks and very rarely used it for small operations.[1] This is because they faced multiple issues like memory leaks.
To replace AsyncTasks, the documentation indicates that java.util.concurrent should be used or Kotlin Concurrency Utilities. These are Kotlin coroutines, are recommended solution for Async code. So, in this article I am going to define what Coroutines are in Kotlin and how to use them. Also, you can find an appendix at the end of this article with links to continue learning about this interesting topic.
Kotlin Coroutines
Coroutines are neither exclusive to Kotlin nor a new concept. Coroutines exist in different languages and some of them have even better coroutines than Kotlin. But, Kotlin is the first language to allow Android Developers to use them.
A Coroutine part of a software that can be suspended and resumed, allowing developers Asynchronous or non-blocking programming. This is important in mobile apps because one of the goals is to keep the UI (main-thread) free from complex operations and allow it to be responsive enough for the user.
If the main-thread continues to be blocked, an ANR (Application not responding) will appear to the user, leading to a bad user experience. So, it’s critical to leave all the complex and time consuming tasks (such as downloading) to run asynchronously.
Coroutines are provided to Kotlin by a first party library: kotlinx.coroutines. This is a rich library for coroutines developed by JetBrains. Kotlin coroutines are simple to read, provide cancellation support and they can work really well with the app component life cycles.

This is one of the best examples of when to use a Coroutine. Reading from a database is a costly operation and it can take a long time to block the UI. As you can see in the code above, the code is readable and quite short. But, nonetheless, it is a coroutine. Pay attention to the suspend keyword before the function. What does it mean?
Basically, suspending functions are functions that can be suspended (paused) and resumed. This is useful because they can execute long running operations without blocking. The suspended coroutine lets the thread complete other routines and then resume. I hope by now you start seeing how useful this is.
The suspend keywords also make this method only callable from a Coroutine Scope. CoroutineScope is in charge of determining where the coroutines should run[1]. Let’s see the previous code again as an example, this is going to read the top ten books from the disk. This operation can block the UI, so we need to make it run in a dispatcher other than the Main Dispatcher.
But enough theory, let’s put our computers in action.
Step 1: Add the dependencies.
Let’s create a simple, single activity app. We’ll use this one to experiment with coroutines. This example is a simplified approach on how to use coroutines.
The first thing we need to do is add the corresponding dependencies for coroutines to work in our project.

Step 2: Hello World!
Let’s start with the classic. Create a function firstCoroutine() like this one:
fun firstCoroutine() { CoroutineScope(Dispatchers.Main).launch { delay(1000) Log.d("Coroutines", "World!") } Log.d("Coroutines", "Hello ") }
It’s ok if you call this function from your onCreate() method, the idea is to get coroutines.
Let’s break this down.
The first line in the functions calls a CoroutineScope, it wraps the coroutine and as I mentioned before, it decides where the coroutine is going to run, in this case it is the Dispatcher.Main. This dispatcher is confined to the Main Thread. Then, we have the launch function which receives the coroutine code that will be invoked in the context of the provided scope. In simple words, the code inside the curly braces after launch are your coroutines and, this is the code that will execute without blocking the Main Thread.
To test this, our first line is a delay. The coroutine will wait 1000 milliseconds, without blocking the UI. Then, it will log “World!” As you can see, we are logging hello as well. By now you can guess in which order these messages are going to be logged, but run the app anyways.

You just executed a coroutine.
Step 3: Network Operations.
Remember to add the Internet permissions in the manifest.
For this example, let’s borrow some functions from the Android developer’s tutorial on how to connect to the network.
/** * Converts the contents of an InputStream to a String. */ fun readStream(stream: InputStream, maxReadSize: Int): String? { val reader: Reader? = InputStreamReader(stream, "UTF-8") val rawBuffer = CharArray(maxReadSize) val buffer = StringBuffer() var readSize: Int = reader?.read(rawBuffer) ?: -1 var maxReadBytes = maxReadSize while (readSize != -1 && maxReadBytes > 0) { if (readSize > maxReadBytes) { readSize = maxReadBytes } buffer.append(rawBuffer, 0, readSize) maxReadBytes -= readSize readSize = reader?.read(rawBuffer) ?: -1 } return buffer.toString() }
The readStream() function is going to stay as it is.
@Throws(IOException::class) private suspend fun downloadUrl(url: URL) = Dispatchers.IO { var connection: HttpsURLConnection? = null return@IO try { connection = (url.openConnection() as? HttpsURLConnection) connection?.run { readTimeout = 3000 connectTimeout = 3000 requestMethod = "GET" doInput = true connect() if (responseCode != HttpsURLConnection.HTTP_OK) { throw IOException("HTTP error code: $responseCode") } inputStream?.let { stream -> readStream(stream, 500) } } } finally { // Close Stream and disconnect HTTPS connection. connection?.inputStream?.close() connection?.disconnect() } }
You can notice at the declaration of the function that is equals to Dispatechers.IO. We’ll get to the details in a moment.
This pair of functions are simple enough to download data from a URL. For this example, we are going to use one from JSONPlaceholder. In case you aren’t familiar with this, it’s a free online REST that you can use whenever you need some fake data that you could use as a placeholder.
We are going to download this url : https://jsonplaceholder.typicode.com/todos/1
Which contains this json:
{ "userId": 1, "id": 1, "title": "delectus aut autem", "completed": false }
First, let’s declare the following function
private suspend fun getDataFromNetwork(url: String) { val result = downloadUrl(URL(url)) textViewData.text = result }
Then, in our onCreate() function we are going to do the following call.
CoroutineScope(Dispatchers.Main).launch { val url = "https://jsonplaceholder.typicode.com/todos/1" getDataFromNetwork(url) }
Wait a Minute. Isn’t this calling the function getDataFromNetwork() in the Main thread?
Yes, it is. But worry not.
While getDataFromNetwork() is running on the UI Thread, the downloadUrl() is not. getDataFromNetwork() will be suspended until the data is available and then it will interact with the UI to update it.
That’s why it is running on the UI Thread. On the other hand, in the function definition, we set the launch mode of downloadUrl() to be on the IO Dispatcher. When we do this, the information we want to return must be returned using return@IO. If this would be the Default Dispatcher, the information would be returned as return@Default and so on.
Conclusion
As you can see, the basics of coroutines are not complicated. With this article, I barely touched the surface but it is enough to throw some light on the world of coroutines. You should be able to start replacing the AsyncTask with coroutines with this.
Appendix
The Android Team continues to create new articles that can help you to understand how to integrate Coroutines with Android components and the best practices. You should take a look here: https://medium.com/androiddevelopers/tagged/kotlin%20coroutines
_______________________
https://medium.com/@trishantsharma1997/asynctask-in-android-f594a565d676
https://kotlinlang.org/docs/reference/coroutines/coroutines-guide.html
https://developer.android.com/training/basics/network-ops/connecting