Coroutine in Android: Basic

Abir Hasan Zoha
AndroidPub
Published in
8 min readJul 30, 2019
Image by Michelle Maria from Pixabay

In this series of blog posts, I am going to explore Kotlin Coroutine and it’s usage in the Android world.

  1. Coroutine in Android: Basic (You are here)
  2. Coroutine in Android: Working with Lifecycle
  3. Coroutine in Android: Working with Retrofit (credit: zsmb13)
  4. Coroutine in Android: Working with Room Database
  5. Coroutine in Android: Working with Workmanager

Android development is evolving rapidly. Android development with java was full with so much boilerplate code. By the introduction of Kotlin, now we can be more focused on business logic. Same goes to the Kotlin coroutine. Before coroutine, we had to write so much boilerplate code to run asynchronous business logic. Now with a coroutine, we can write asynchronous and non-blocking code as if it was synchronous and blocking code.

What is Kotlin Coroutine

“A coroutine is an instance of suspendable computation, conceptually similar to a thread, in the sense that it takes a block of code to run and has a similar life-cycle, it is created and started, but it is not bound to any particular thread. It may suspend its execution in one thread and resume in another one. Moreover, like a future or promise, it may complete with some result or exception.”

Kotlin coroutine is a lightweight thread. Coroutines are way cheaper than actual Java Thread. So a developer can fire and forget a long number of coroutine without any headache. Coroutines are more like a task that can be executed in any thread.

Coroutine is a Kotlin language feature. But Kotlin standard library provides only minimal low-level APIs by which other libraries can utilize Coroutine feature. kotlinx.coroutines is a rich library for coroutines, developed by JetBrains. It contains a number of high-level coroutine-enabled primitives. If you want to add kotlinx.coroutnes in your project add the following gradle dependencies:

dependencies { 
...
def coroutines_version = "1.3.0-M2"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
...
}

suspend is the main keyword you will encounter to work with Kotlin Coroutine. suspend keyword is provided by the Kotlin standard library. Let’s discuss the keyword

What is suspend

suspend is a Kotlin keyword which can be used before any kind of function. Any function marked with suspend keyword known as suspending function. Long-running (blocking) suspending function can be suspended from a thread and after completing the task on other thread can be resumed later on the caller thread.

How suspending function works. (image credit)

In the example above, get(…) will suspend the Coroutine before it starts the network request. The function get(...) will still be responsible for running the network request off the main thread. Then, when the network request completes, instead of calling a callback to notify the main thread, it can simply resume the Coroutine it suspended.

Enough talk. Now let’s write code with suspend and Coroutine.

Defining suspending function

Here delay(...) is a suspending function that operates the same as Thread.sleep(...). There is also an indication on the left of delay(millis) that this is a suspending function.

Calling Suspending Function

Here if we remove suspend keyword from logLater(…) function then we can’t call delay(...). Because delay(...) is a suspending function and it is illegal to call suspending function from any normal function. Suspending functions can only be called from other suspending function and from Coroutine. To bridge the non-blocking world with blocking world we have to create Coroutine using coroutine builder. The most used coroutine builder is launch(...) function which is an extension function of CoroutineScope class.

Suspending functions can only be called from other suspending function and from Coroutine.

A new coroutine has been created by GlobalScope.launch{..}

Changing Thread in Coroutine

Suspending function does not change Thread automatically. It is our responsibility to change Thread when we need. But in the Coroutine world, we don’t use Thread directly. We use CoroutineDispatcher to tell Coroutine to change the Thread. CoroutineDispatcher is responsible to determine what thread the corresponding coroutine uses for its execution.

The lunch(..) function takes an optional CoroutineDispatcher parameter to change the Thread.

Dispatchers.Main has been used in the launch(..) function

We can also change the Dispatcher inside any suspending function using withContext(...) function. withContext(...) function returns the value of the last expression.

Thread has been changed using Dispatcher
Result of the above code. you can find that the Dispatcher of the Coroutine has not been changed in the logLater function but changed inside of the withContext{..}

There are Three CoroutineDispatcher in the kotlinx.Coroutine library. They are:

+-----------------------------------+
| Dispatchers.
Main |
+-----------------------------------+

| Main thread on Android, interact |
| with the UI and perform light |
| work |
+-----------------------------------+
| - Calling suspend functions |
| - Call UI functions |
| - Updating LiveData |
+-----------------------------------+
+-----------------------------------+
| Dispatchers.
IO |
+-----------------------------------+

| Optimized for disk and network IO |
| off the main thread |
+-----------------------------------+
| - Database* |
| - Reading/writing files |
| - Networking** |
+-----------------------------------+
+-----------------------------------+
| Dispatchers.
Default |
+-----------------------------------+

| Optimized for CPU intensive work |
| off the main thread |
+-----------------------------------+
| - Sorting a list |
| - Parsing JSON |
| - DiffUtils |
+-----------------------------------+

In Android, you normally interact with the UI which can’t be modified from other thread. So I recommend using Dispatchers.Main in the Coroutine builder. And if you need to change Thread then do this using withContext(...) inside Coroutine or any suspending function. And try not to touch UI elements from suspending function. Try to work with UI elements only from parent Coroutine.

Concurrency in Coroutine

Suspending functions are sequential by default. The codes inside suspending functions and Coroutine get executed one after another & sequentially. If we see the logLater(...) suspending function, at first the first log get executed then it waits for the delay and then the second log gets executed. Everything in suspending world is sequential unless we tell explicitly to run the code concurrently.

To run code concurrently we need to use the async(...) builder method. The async(...) returns Deferred object and by using this object we can wait for the result. async(...) is an extension function on CoroutineScope class.

Coroutine without concurrency
Result of the above code. It takes 10s to complete the code
Coroutine with concurrency. coroutineScope{...} block has been used here to use async(...) inside a suspending function.
Result of the above code. It takes only 6s to complete. Also, notice second log message has been executed immediately after the first log message

If we see the log message we can find that without concurrency it took 10 seconds to complete the task but with async(...) function and explicit concurrency it took only 6 seconds to complete.

Notice: coroutineScope{...} block has been used to call async(...) inside a suspending function.

async(...) can be called in many ways but it is recommended to call it only inside lunch{..} or coroutineScope{...} block.

calling async(...) inside lunch{...} block. this is same as calling inside coroutineScope{...} block

CoroutineScope

By this time you should have noticed CoroutineScope multiple time. I have used GlobalScope to launch new coroutine which is an implementation of CoroutineScope. Now it is high time to learn about this.

I think of CoroutineScope as a boundary of the coroutines. CoroutineScope helps us to maintain the life-cycle of the coroutines. It has some extension functions by those we can easily create and cancel coroutines.

As of now, we have seen two coroutine builders launch(...) and async(...). Both of them are defined as extension function of CoroutineScope class to encourage developers to structure their coroutines.

fun <T> CoroutineScope.async(...): Deferred<T> {...}
fun <T> CoroutineScope.launch(...): Job {...}

To cancel coroutine we have an extension function:

fun CoroutineScope.cancel(...): Unit {...}

Structured Concurrency

If you have read some blog posts or viewed some videos explaining Coroutine you most probably heard about the term “Structured Concurrency”.

To me, Structured Concurrency is the coding paradigm; it is the style of writing coroutine so that we have a minimum headache to maintain and understand the life-cycle of coroutine in our codebase.

If you are using any CoroutineScope other than GlobalScope and you call async(...) only inside launch{...} or coroutineScope{...} block then you are writing coroutines with Structured Concurrency. And make sure you do not have Deferred object outside of coroutine.

Using GlobalScope may lead to a memory leak.

In the above example, I have used GlobalScope for simplicity. But in structured concurrency, you are highly discouraged to use GlobalScope. Using GlobalScope may lead to a memory leak. Luckily in Android, we don’t need to write our own custom CoroutineScope because Android Jetpack already provides us all the CoroutineScope we need. In the second blog post I am going to write about this.

Visit here to learn more about Structured Concurrency.

Coroutine API Annotations

While we are familiar with Alpha, Beta and Stable release, the kotlinx.coroutines library takes a different approach to define its API stability. There are some annotations that indicate the state of the API.

+-----------------------------------+
| ExperimentalCoroutinesApi |
+-----------------------------------+

| there is a chance that those |
| declarations will be deprecated |
| in the near future |
+-----------------------------------+
+-----------------------------------+
| ObsoleteCoroutinesApi |
+-----------------------------------+

| these declarations will be |
| deprecated in the future but |
| there is no replacement for |
| them yet |
+-----------------------------------+
+-----------------------------------+
| InternalCoroutinesApi |
+-----------------------------------+

| should not be used outside of |
| `kotlinx.coroutines` |
+-----------------------------------+
+-----------------------------------+
|
FlowPreview |
+-----------------------------------+

| Marks [Flow]-related API as a |
| feature preview Flow preview |
| has **no** backward compatibility |
| guarantees |
+-----------------------------------+

There is also an interesting twitter thread about Coroutine API annotations which you may find helpful.

In this blog post, I have documented my understanding of Kotlin Coroutine. If you find it helpful please encourage me by clapping ❤️ & following 😄. And feel free to comment if you have any observation or suggestion.

Follow me on => Twitter :: LinkedIn :: Github

--

--