Skip to main content

Coroutine- A complete tutorial

 What are coroutines?

To know about coroutine, first you need to know about asynchronous programming, thread and multithreading concept. What is a thread? Thread describes in which context the function or sequence of instructions should be executed. So, every block of code or functions runs in a thread, right? Also, you can load multiple threads to perform different block of codes 

How to start a thread?

Thread thread = new Thread(){

    public void run(){

      System.out.println("This is a thread");

    }

  }

   thread.start();

So when coming to android, before learning about coroutines we need to discuss some scenarios. Normally how an app works when user launch an application. When user launchers the application, a main thread is created. This thread is intended to do small operations like button clicks, UI interaction , small mathematical operations. We cant perform long running operation like file download, database queries, network operations and image loading in the main thread. If we try to run this in our main thread, the main thread will be blocked . So we need to do this operations asynchronously without blocking the UI with the help pf thread.  In earlier cases we achieved this with thread and traditional callback events

So what is coroutine. It is a concurrency design pattern that helps us to run the code asynchronously. So is it a thread? No, but it behaves like a thread, a light weight thread. Consider what happened if you run a 10000 thread in your app. The app will get crashed with out of memory error. But with coroutine you can handle this efficiently. 

Coroutine is not a thread

  • It is a light weight thread
  • Coroutines are cheap, we can create thousands of coroutine without any memory issues. 
  • It is executed inside a thread, we can start may coroutine in one thread
  • They are suspendable ie they can wait for each other, they can communicate each other, they can run in parellel
  • They can switch their context, ie coroutine started from one thread can switch to other thread

How to write a coroutine ?

We will discuss each terms in this blog in detail. Before that we can check how a coroutine works on  a background thread. 

How coroutine works on a background thread
Log.d(TAG,"Main thread ${Thread.currentThread().name}")
thread{
   
Log.d(
TAG,"thread ${Thread.currentThread().name}")
}
scope.launch {//creates a coroutine that runs on a background thread
   
Log.d(TAG,"Coroutine thread ${Thread.currentThread().name}")
}
scope.launch {
   
Log.d(TAG,"Coroutine second thread ${Thread.currentThread().name}")
}

See the output

Suspend Function

Suspend functions are like other functions but suspend function can be called only from another suspend function or from the coroutine. The function delay() we used in the above example was an example of suspend function. 

private suspend fun doNetworkOperation(){
delay(1000L)
Log.d(TAG,"This is a suspend function")
}
GlobalScope.launch(Dispatchers.Main) {
//calling suspend function
doNetworkOperation()
}

Coroutine Builders

Coroutine builders are the functions used to create a coroutine. Main coroutine builders are launch(), async() and runBlocking()

launch()

This is a fire and forget coroutine function. Means it will start a coroutine but will not return any result to the coroutine. It will return an instance of Job class which can be used to handle the launched coroutine and  can be used to wait for its completion using join() function

val job=  GlobalScope.launch{
delay(1000L)
}
runBlocking {
job.join()//wait until child coroutine completes
Log.d(TAG,"job 1 completed")
}

Launch function is an extension function on the CoroutineScope interface which is used to ensure structured concurrency. Structured concurrency and CoroutineScope can be discussed later in this blog.

async()

If you want to perform a background operation and do something from the result of the background operation, then you can use async function. Async function will return the instance of the Deffered<T> class which has as await function which will return the result to the coroutine once it is ready

//val result:Deffered<Int>
val result= GlobalScope.async {
val sum=10+5
sum
}
runBlocking {
val sum=result.await()
Log.d(TAG,"sum is $sum")
}

runBlocking()

This coroutine builder will run on the main UI thread and it will block the UI thread until it finished the tasks created by the coroutine. It doesnt need any scope to start the coroutine and will block the entire UI interaction. We can acheive the same functionality using a launch function with Disptacher.Main without blocking the UI. So then what will be the purpose of runBlocking? 


runBlocking {
   
delay(
100L)
    Log.d(
TAG,"Coroutine runBlocking  ${Thread.currentThread().name}")
}

With GlobalScope launch function

GlobalScope.launch(Dispatchers.Main) {
   
delay(
100L)
    Log.d(
TAG,"Coroutine global scope with dispatcher main  ${Thread.currentThread().name}")
}

The main usecase of runBlocking is for testing with jUnit . Another use case if if you want to call a suspend function from the main thread without having coroutine properties ,then also you can create a runBlocking code. 

Structured Concurrency & Scopes in Coroutine

It is a pattern that follow a structure to write asynchronous or concurrent code so that the child operations will complete its tasks before their parents. Every coroutine in kotlin should be created from a CoroutineScope which delimits the lifetime of the coroutine


Comments

Popular posts from this blog

Capture image without surface view as background service in android

Capture image without user knowledge in android/Capture image as background service in android/Take photo without preview in android/ Camera without preview in android  package com.example.picture; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import android.app.Service; import android.content.Context; import android.content.Intent; import android.hardware.Camera; import android.hardware.Camera.Parameters; import android.media.AudioManager; import android.os.IBinder; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; public class CameraService extends Service {       //Camera variables       //a surface holder       private SurfaceHolder sHolder ;        //a variable to control the camera       private Camera mCamera ;   ...

Custom Spinner in Android

Custom Spinner in Android/Spinner using custom adapter/spinner with icon and text  In this tutorial, i'll show you to customizing spinner in different methods.  To change the normal display of spinner view, add background image using the code " android:background="@drawable/myspinner_background" in the xml. You can also make it customizable by setting up an xml file in drawable folder Custom display with icon in the spinner   main.xml <? xml version = "1.0" encoding = "utf-8" ?> < LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android"     android:orientation = "vertical"     android:layout_width = "fill_parent"     android:layout_height = "fill_parent"     >     < Spinner     android:drawSelectorOnTop = "true"     android:id = "@+id/spinner1"     android:layout_width = "match_parent"     and...