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
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}")
}
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
Post a Comment