Copyright — https://www.techyourchance.com/wp-content/uploads/2020/11/coroutines-cheat-sheet.jpg

Coroutines — Android Development

Shivansh Seth

--

1. Kotlin Coroutines

A Coroutine is a concurrency design pattern that you can use on Android to simplify code that executes asynchronously.

While running a huge number of functions, hell lot of threads would be in use and some might be free after their process execution. So, in Kotlin concept of Co-routines is discovered. It is basically a pool of threads, then each co-routine is scheduled on a thread and co-routines re-utilizes free threads.

Kotlin coroutines on Android | Android Developers

Coroutines | Kotlin

When using runBlocking each concurrent thread works and then the main() function ends it task. Here a suspend function is used inside a co-routine. Also, this schedule works in a Round-Robin Fashion, this is so because this kind of scheduling works at the compiler level.

Also, on increasing the number of launch , the number of Dispatchers are not going to increased. The threads will remain same, until errors occur.

val numList : ArrayList<Int>
fun main(){
for(i in 0 until 10000){
numList.add(i)
}

runBlocking{
launch{printList("1")}
launch{printList("2")}
launch{printList("3")}
}
}
suspend fun printList(id: String){
for(i in 0 until 10000){
if(i%100==0){
withContext(Dispatchers.IO){
println(" $id $i")
}
}
}
}

Also, we can use GlobalScope for the same task, the problem that occurs is that the main() function executes way faster than the working of the coroutines.

val numList : ArrayList<Int>
fun main(){
for(i in 0 until 10000){
numList.add(i)
}

GlobalScope.launch{
launch{printList("1")}
launch{printList("2")}
launch{printList("3")}
}
}
suspend fun printList(id: String){
for(i in 0 until 10000){
if(i%100==0){
withContext(Dispatchers.IO){
println(" $id $i")
}
}
}
}

Now, if I want to wait for a particular thread to execute and then let other threads start working, I can use async{println("1")}.awit()

val numList : ArrayList<Int>
fun main(){
for(i in 0 until 10000){
numList.add(i)
}

runBlocking{
async{printList("1")}.await()
launch{printList("2")}
launch{printList("3")}
}
}
suspend fun printList(id: String){
for(i in 0 until 10000){
if(i%100==0){
withContext(Dispatchers.IO){
println(" $id $i")
}
}
}
}

2. Suspend Functions

Now, we will be focusing on the thread on which the process is being executed using that function Thread.currentThread().name .

Here, multiple threads are provided with the tasks. Each task or looper has its own thread to work upon.

val numList : ArrayList<Int>
fun main(){
for(i in 0 until 10000){
numList.add(i)
}
Thread{printList("1")}
Thread{printList("2")}
Thread{printList("3")}
Thread{printList("4")}
Thread{printList("5")}
Thread{printList("6")}
Thread{printList("7")}
Thread{printList("8")}
Thread{printList("9")}
Thread{printList("10")}
Thread{printList("11")}
Thread{printList("12")}
}
suspend fun printList(id: String){
for(i in 0 until 10000){
if(i%1000==0){
println("looper $id iteration $i thread ${Thread.currentThread().name}")
}
}
}

Output -

If we’re using the function [Dispatcher.IO](<http://Dispatcher.IO>) then each of the looper is working on different Dispatcher-worker.

val numList : ArrayList<Int>
fun main(){
for(i in 0 until 10000){
numList.add(i)
}

runBlocking{
launch{printList("1")}
launch{printList("2")}
launch{printList("3")}
launch{printList("4")}
launch{printList("5")}
launch{printList("6")}
}
}
suspend fun printList(id: String){
for(i in 0 until 10000){
if(i%1000==0){
withContext(Dispatchers.IO){
println("looper $id iteration $i thread ${Thread.currentThread().name}")
}
}
}
}

Output -

Here, if you Threads instead of using Coroutines then every looper works on the main thread.

val numList : ArrayList<Int>
fun main(){
for(i in 0 until 10000){
numList.add(i)
}

runBlocking{
launch{printList("1")}
launch{printList("2")}
launch{printList("3")}
launch{printList("4")}
launch{printList("5")}
launch{printList("6")}
}
}
suspend fun printList(id: String){
for(i in 0 until 10000){
if(i%1000==0){
println("looper $id iteration $i thread ${Thread.currentThread().name}")
}
}
}

Output -

3. Threads vs Dispatcher

Here it will take 2000 milliseconds to execute both of the functions. First task1() will be executes and then task2() will be executed.

fun main(){
task1()
task2()
}
private fun task1(){
println("Starting task 1 on ${Thread.currentThread().name}")
Thread.sleep(1000)
println("Ending task 1 on ${Thread.currentThread().name}")
}
private fun task2(){
println("Starting task 2 on ${Thread.currentThread().name}")
Thread.sleep(1000)
println("Ending task 2 on ${Thread.currentThread().name}")
}

Output -

We can use this similar kind of thing on Co-routines. Also, it would take less time even while both of them contains 1000 milliseconds of delay because here both the task1() and task2()executes simultaneously.

fun main(){
runBlocking{
launch{task1()}
launch{task2()}
}
}
private suspend fun task1(){
println("Starting task 1 on ${Thread.currentThread().name}")
delay(1000)
println("Ending task 1 on ${Thread.currentThread().name}")
}
private suspend fun task2(){
println("Starting task 2 on ${Thread.currentThread().name}")
delay(1000)
println("Ending task 2 on ${Thread.currentThread().name}")
}

Output -

And now if we used Threads instead of using Coroutines then they will start and end at the same time despite of having a delay of 1000 milliseconds as they will be running simultaneously.

fun main(){
println("Start = ${System.currentTimeMillis()}")
thread{ task1({println("End = ${System.currentTimeMillis()}") })}
thread{ task2({println("End = ${System.currentTimeMillis()}") })}
}
private suspend fun task1(onEnd -> Unit()){
println("Starting task 1 on ${Thread.currentThread().name}")
Thread.sleep(1000)
println("Ending task 1 on ${Thread.currentThread().name}")
onEnd()
}
private suspend fun task2(onEnd() -> Unit){
println("Starting task 2 on ${Thread.currentThread().name}")
Thread.sleep(1000)
println("Ending task 2 on ${Thread.currentThread().name}")
onEnd()
}

Output -

--

--

No responses yet