Before start work on the coroutine, First, we have to know about the most common component using in a coroutine. Let’s start with some components which are basically used in a coroutine.

Coroutine Scope:

1. ViewModelScope

implementation “androidx.lifecycle:lifecycle-viewmodel-ktx:$view_model_scope_version”

This library adds viewModelScope as an extension function and binds the scope to Dispatchers.Main by default. However, the dispatcher can be changed if need be. The job is also automatically canceled when the ViewModel is cleared, so all we have to do is this:

class MainViewModel : ViewModel()
{
private fun doSomething()
{
viewModelScope.launch(Dispatchers.Default)
{ //Specify dispatcher if you like
// Coroutine is launched. Time to do something.
}
}
// No need to override onCleared, it's taken care of :)
}

2. LifeCycleScope

implementation “androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_scope_version”

Launching a coroutine using this scope can be done like this:

class HomeFragment: Fragment() {   
...
private fun doSomething()
{
lifecycleOwner.lifecycleScope.launch {
// Coroutine launched. Do some computation here.
}
}
}

3. Global Scope

class HomeFragment: AppCompatActivity() {   
...
private fun doSomething()
{
GlobelScope.lifecycleScope.launch {
// Coroutine launched. Do some computation here.
}
}
}

Coroutine Builder

1. runBlocking

2. launch

3. async

runBlocking

We typically runBlocking to run tests on suspending functions. While running tests, we want to make sure not to finish the test while we are doing heavy work in test suspend functions.

launch

You can create a new coroutine from launch as follows:

import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
fun main() {
println("Starting main function..")
GlobalScope.launch {
println(doSomething())
}
runBlocking {
delay(4000L) // make sure to keep the JVM alive in order to wait for doSomething() to execute
}
}
suspend fun doSomething() : String {
delay(3000L) // simulate long running task
return "Did something that was 3 seconds long"
}

In the above code, we call the suspending function, doSomething() and after 3 seconds of delay we print the string: “Did something that is 3 seconds long”.

Note: “delay” in Kotlin, is a suspend function which delays a coroutine without blocking the current thread. It then resumes the coroutine after the specified time (In our case, 3 seconds or 3000 milliseconds).

async

async is a coroutine builder which returns some value to the caller. async can be used to perform an asynchronous task which returns a value. This value in Kotlin terms is a Deferred<T> value.

We can simply use async to create a coroutine doing some heavy work like so:

import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
fun main() {
println("Hello, ")
GlobalScope.async {
doSomething()
}

runBlocking {
delay(3000L) // only used to keep the JVM alive
}
}
suspend fun doSomething() {
delay(2000L)
print("this is bad example for using async!!!")
}

The output for the above code would be, “Hello, this is a bad example for using async!!!”.

However, if doSomething() function doesn’t return anything, what is its use?

I have created the above example precisely to point out this error. It is highly discouraged to use async when you have nothing to return. Using launch should suffice in this case, like so:

import kotlinx.coroutines.*fun main() {
println("Hello, ")
GlobalScope.launch {
doSomething()
}
runBlocking {
delay(3000L) // only used to keep the JVM alive
}
}
suspend fun doSomething() {
delay(2000L)
print("this is an example for using launch when async doesn't return anything!!!")
}

Coroutine Context

All of the coroutine classes implement CoroutineScope and have the property coroutineContext. Therefore, we can access coroutineContext in the coroutine block:

runBlocking {
Assertions.assertNotNull(coroutineContext)
}

And we can read an element of a coroutineContext:

runBlocking {
Assertions.assertNotNull(coroutineContext[Job])
}

Scope and Context are very similar only difference is that scope used to create and manage coroutine. Whereas Context is the set of variables and data associate with that coroutine.

An important element of Coroutines Context

  • Job — It basically handles the coroutine lifecycle. This concept also I’ll write separate articles on that.

Let’s discuss more about that topic in part-2 soon.

Thanks for reading!

Senior Software Engineer | Android | Java | Kotlin|Xamarin Native Android|Flutter