Kotlin coroutines on Android

Deepak Sikka
6 min readMay 11, 2021

This Story is from the Kotlin- Series, now we will learn basic of coroutines.

What is a coroutine?

You can think of a coroutine like a lightweight thread, but one key difference is that a coroutine can be suspended without blocking the thread it is running in. Many coroutines can exist in the same thread. You can update the function signature with suspend, which means that function can only be called from a coroutine or another suspend function.In order to create a coroutine, you will typically use an extension function on CoroutineScope, or use a coroutine scope coming from the Android framework (e.g. ViewModel or LifecycleOwner scope). In either case, you have the option to specify the dispatchers.

Coroutine is a concurrency design pattern that you can use on Android to simplify code that executes asynchronously. Coroutines were added to Kotlin in version 1.3 and are based on established concepts from other languages.

Features

Coroutines is our recommended solution for asynchronous programming on Android. Noteworthy features include the following:

  • Lightweight: You can run many coroutines on a single thread due to support for suspension, which doesn’t block the thread where the coroutine is running. Suspending saves memory over blocking while supporting many concurrent operations.
  • Simplified asynchronous calls.
  • State machine object that means data is available for each state.
  • Ability to wait patiently- tasks executes sequentially and coroutines can wait for the result.
  • Fewer memory leaks: Use structured concurrency to run operations within a scope.
  • Built-in cancellation support: Cancellation is propagated automatically through the running coroutine hierarchy.

Now,we are going see the steps involved in adding the stable version of Coroutines included in Kotlin v1.3.

Step 1.

Go to Tools → Kotlin → Configure Kotlin Plugin Updates, select “Stable” in the Update channel drop-down list, and then click Check for updates.

Step 2. Add following lines to your project level build.gradle

dependencies {
//...
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.31'
}

Step 3. Add following lines to your app/module level build.gradle
We are adding coroutines-core along with coroutines-android.

dependencies{
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0"
}

Now, sync your project with gradle files and you are ready use the latest Coroutines.

Examples overview

Create kotlin class name it as CoroutinesDemo.In this,class i have main function to execute the exampleBlocking() function.In exampleBlocking() function in first line i just simply print some value,than in second line we are using printDelayed function which has string parameters. In printDelayed() we performed delay(1000) than print the message which we are receving from parameters. To used delay function we have to use suspend fun before function name. Now question is what is suspend in coroutines.

fun main(args: Array<String>) {
exampleBlocking()
}
suspend fun printDelayed(message: String) {
//Thread.sleep(1000)
delay(1000)
println(message)
}

fun exampleBlocking(){
println("one")
runBlocking {
printDelayed("two")
}
println
("three")

}
//other Way of using runBlocking
fun exampleBlocking()=runBlocking{
println("one")
printDelayed("two")
println("three")

}

Suspending functions are at the center of everything coroutines. A suspending function is simply a function that can be paused and resumed at a later time. They can execute a long running operation and wait for it to complete without blocking.

Imagine it working like this:

So how does it work? Instead of returning just result, a suspending function also returns context which is used by the caller. In simple words, it is a function which executes a part of the code and then suspends while keeping the context and reference to the remaining code (like a lambda consisting remaining code). So whenever the function resumes it has the context and the remaining code that need to be executed.

Most important point suspending functions can only be invoked by another suspending function or within a coroutine.

Now question is what is runBlocking and how we can use it.

  • runBlocking { } starts a Coroutine in blocking way. It is similar to how we were blocking normal threads with Thread class and notifying blocked threads after certain events.
  • runBlocking { } does block the current executing thread, until the coroutine (body between {}) gets completed.

CoroutineDispatcher

Coroutine Dispatchers are use to define thread,where coroutines should run.CoroutineDispatcher can restrict coroutine execution to a specific thread, dispatch it to a thread pool, or let it run unrestricted. It means CoroutineDispatcher determines what thread or threads the corresponding coroutine uses for its execution.

1.aDispatchers.Default — is used by all standard builder if no dispatcher nor any other ContinuationInterceptor is specified in their context. It uses a common pool of shared background threads. This is an appropriate choice for compute-intensive coroutines that consume CPU resources.

1.b. Dispatchers.IO — uses a shared pool of on-demand created threads and is designed for offloading of IO-intensive blocking operations (like file I/O and blocking socket I/O).

1.c. Dispatchers.Unconfined — starts coroutine execution in the current call-frame until the first suspension. On first suspension the coroutine builder function returns. The coroutine resumes in whatever thread that is used by the corresponding suspending function, without confining it to any specific thread or pool.

  1. d. Dispatchers.Main — when there is need of publishing data on UI from background thread, simply create coroutine launch using Dispatchers.Main.

Examples overview

fun main(args: Array<String>) {
exampleBlockingDispatcher()
exampleLaunchGlobal()
}
suspend fun printDelayed(message: String) {
delay(1000)
println(message)
}
// Using Dispatchers
fun exampleBlockingDispatcher() {
runBlocking
(Dispatchers.Default) {
println("one -from thread ${Thread.currentThread().name}")
printDelayed("Two -from thread ${Thread.currentThread().name}")
}
println("Three -from thread ${Thread.currentThread().name}")
}

When we are using dispathers than coroutine mat switches to different thread most of time.But it is still blocking the main thread.Than use GlobalScope.launch as show in below code:

// Using GlobalScope.launch
fun exampleLaunchGlobal() = runBlocking {
println("one -from thread ${Thread.currentThread().name}")
GlobalScope.launch {
printDelayed("Two -from thread ${Thread.currentThread().name}")
}
println("Three -from thread ${Thread.currentThread().name}")
delay(3000)

}

In above code i am using delay of (3000) but in real application we cannot judge how much time it will take to execute this logic than this delay may fails. To avide this we have to decalared a GlobalScope in val and than we have to use join() fun as performed in below code

fun main(args: Array<String>) {
exampleLaunchGlobalWaiting()
}
suspend fun printDelayed(message: String) {
delay(1000)
println(message)
}
// Using Wait
fun exampleLaunchGlobalWaiting() = runBlocking {
println("one -from thread ${Thread.currentThread().name}")
val job = GlobalScope.launch {
printDelayed("Two -from thread ${Thread.currentThread().name}")
}
println("Three -from thread ${Thread.currentThread().name}")
job.join()
}

We can also created our custom Dispathers for that we have to use below code:

fun exampleLaunchCoroutineScope() = runBlocking {
println("one -from thread ${Thread.currentThread().name}")
// For creating custom dispatcher
val customDispatcher = Executors.newFixedThreadPool(2).asCoroutineDispatcher()
launch
(customDispatcher) {
printDelayed("Two -from thread ${Thread.currentThread().name}")
}
println("Three -from thread ${Thread.currentThread().name}")
// For Shutdown custom dispathers we have use below code:(customDispatcher.executor as ExecutorService).shutdown()
}

Using CoroutineScope :

fun main(args: Array<String>) {
exampleLaunchCoroutineScope
()
}

suspend fun printDelayed(message: String) {
delay(1000)
println(message)
}
fun exampleLaunchCoroutineScope() = runBlocking {
println("one -from thread ${Thread.currentThread().name}")
this.launch {
printDelayed("Two -from thread ${Thread.currentThread().name}")
}
println
("Three -from thread ${Thread.currentThread().name}")
}

Using AsyncAwait :

fun main(args: Array<String>) {
exampleAsyncAwait()
}
suspend fun calculateHardThings(startNum: Int): Int {
delay(1000)
return startNum * 10
}
fun exampleAsyncAwait() = runBlocking {
val startTime = System.currentTimeMillis()

val deferred1 = async { calculateHardThings((10)) }
val deferred2 = async { calculateHardThings((20)) }
val deferred3 = async { calculateHardThings((30)) }

val sum = deferred1.await() + deferred2.await() + deferred3.await()
println("async/await result=$sum")

val endTime = System.currentTimeMillis()
println("Time taken : ${endTime - startTime}")
}
Output:
async/await result=600
Time taken : 1014
Process finished with exit code 0

Using withContext :

fun main(args: Array<String>) {
exampleWithContext()
}
suspend fun calculateHardThings(startNum: Int): Int {
delay(1000)
return startNum * 10
}
fun exampleWithContext() = runBlocking {
val startTime = System.currentTimeMillis()

val result1 = withContext(Dispatchers.Default) { calculateHardThings((10)) }
val result2 = withContext(Dispatchers.Default) { calculateHardThings((20)) }
val result3 = withContext(Dispatchers.Default) { calculateHardThings((30)) }

val sum = result1 + result2 + result3
println("async/await result=$sum")

val endTime = System.currentTimeMillis()
println("Time taken : ${endTime - startTime}")
}
Output:async/await result=600
Time taken : 3022
Process finished with exit code 0

If you liked the article, clap clap clap 👏👏👏 as many times as you can.

LIKED SO MUCH! Medium allows up to 50 claps.

--

--

Deepak Sikka

Senior Android Developer. Working on technology Like Java,Kotlin, JavaScript.Exploring Block Chain technology in simple words.