Mahesh Jadkar
Globant
Published in
6 min readSep 22, 2022

--

Kotlin Coroutines Part -2: Scope & Dispatchers

While working with mobile applications we always need to perform background operations like sending the details over the network or performing long-running operations in the background. We have many libraries to achieve this but today we are going to talk about Kotlin-Coroutines.
We always need to manage the lifecycle of background threads as well as the concurrency. So Kotlin is providing scopes & Dispatchers for it.
Let’s discuss Why we need to use scope and dispatchers?
The traditional approach is to perform async or long-running operations where we are facing issues like memory leaks and managing the life cycles. To overcome this issue coroutine scope was introduced.

What is the scope of coroutines?

As the name implies it helps to define a scope for coroutines, meaning basically it defines the lifespan of the coroutines. The scope keeps track of all the coroutines, which helps us to When a scope cancels, coroutines get canceled. Every coroutine builder like launch, async, etc. is an extension of CoroutineScope. A coroutine cannot be launched without scope.

Coroutines scope helps convention for structure concurrency, so basically, the context of scope contains instances of job which help us to enforce structure concurrency.

Image referred from https://www.freepik.com/premium-vector/digital-marketing-infographic_4510149.htm

We will get a clear idea when we see each scope. Let’s see the next topic: What are the scopes we have? These have listened here

  • GlobalScope
  • Lifecycle Scope
  • ViewModelScope
  • CoroutinesScope
  • MainScope

1. GlobalScope

When the coroutines launch with the GlobalScope, then the life of coroutines binds to the application life cycle. It means coroutines launched with global scope live as long as the application is destroyed.

Real-time use case:

When we want to execute a task that operates during the whole application’s lifetime. For example, fetching configuration from the server or fetching user profile.

Note: The GlobalScope class is now marked with the @DelicateCoroutinesApi annotation. From now on, the use of GlobalScope isn’t recommended. Global scope is used to launch top-level coroutines that operate during the whole application lifetime and are not provisionally canceled. For example, consider the following code:

The above creates a coroutine in GlobalScope that works in the background without any provision to cancel it or to wait for its completion. If the network is slow, it keeps waiting in the background, consuming resources, here due to this it is easy to accidentally create resource or memory leaks.

2. lifecycle scope:

When the coroutines launch with the lifecycleScope, then the life of the coroutine ties with the LifeCycle of Activity or Fragment. It means When the activity or fragment is destroyed then the coroutine also dies.

To access the CoroutineScope of the Lifecycle of activity use lifecycle.coroutineScope or for fragment use lifecycleOwner.lifecycleScope.

Real-time use case:

1. We can use lifecycleScope to perform some UI tasks for example precomputed text and showingToolTipScreen(Success, Error, or Intro slider screen).

2. We might have other use cases where we want to execute a function in a specific lifecycle state and stop or cancel when outside the state, for example, LiveData Observer.

Example:

3. ViewModel Scope

ViewModelScope is defined for every ViewModel. Coroutines in this scope are useful when there is work that should only be done when a ViewModel is active. Using viewModelScope coroutine gets automatically canceled if ViewModel is cleared.

We can use viewModelScope to perform operations like Fetching data from the server, saving records in the database, and so on.

Real-time use case:

1. If we are using a ViewModel and we want to execute a task in the background like performing a network request call. For those kinds of tasks, we will use viewModelScope.

2. I am using MVVM architecture and in the ViewModel doing some background tasks like performing a network request call. So we can use viewModelScope here while launching coroutines.

Example:

In android, we have the built-in scopes in the KTX libraries. These are the life cycle-aware components such as viewModelScope and lifecycleScope. We need to add the appropriate dependencies when using these scopes.

4. CoroutinesScope

This will allow you to define custom scopes with your own coroutines context.

So basically to start a coroutine requires a CoroutineScope and this will take CoroutineContext as a parameter.

Real-time use case:

In some situations when we want to control the lifecycle of the Coroutines we launch so that we can cancel them and handle any exceptions.

E.g:

5. MainScope

It will launch coroutines within the main thread i.e UI thread. It uses Dispatcher.Main for its coroutines.

E.g:

It’s time to move to the next topic i.e Dispatcher.

What is Dispatcher in coroutines?

A dispatcher basically determines which thread the coroutine runs on. If you don’t specify dispatcher then it will be difficult to predict on which thread coroutines runs.
Here is an example.

If we launch above coroutines we can see the thread in which the coroutine is launched cannot be predicted, sometimes it is DefaultDispatcher-worker-1, or DefaultDispatcher-worker-2 or DefaultDispatcher-worker-3 or DefaultDispatcher-worker-4.

However, if you want to be a bit more precise on how yours coroutine runs, you can specify a dispatcher. In android, the application dispatcher plays an important role to manage different types of tasks.

Types of Dispatchers in Coroutines.

So we have a few common dispatchers in Coroutine that we use. These have been listed here.

  • Main Dispatchers
  • Default Dispatchers
  • IO Dispatchers
  • Unconfined Dispatchers

Main Dispatchers :

Main.Dispatcher is mainly used When we want to update or perform UI operations. As we already know that the UI cannot be updated unless you are on the UI thread, which means the main thread.

So let’s go ahead and take an example for better understanding.

Default Dispatchers :

Default dispatchers are very useful for CPU-intensive work. So things like data processing, image processing. That kind of work should usually be done with the default dispatchers.

Example:

Let’s consider we have a list of bank transactions and you want to filter out the transactions list. So hereafter applying filters we need to do data processing and get a filtered list. For this kind of task we use Default dispatchers.

IO Dispatchers :

It is useful for performing network operations, reading/writing files, and Database operations. So any Input Output operations should go through these IO dispatchers.

Let’s go ahead and take an example. Consider fetching data from the server and saving it on local storage.

Unconfined Dispatchers

The Dispatchers.Unconfined starts the coroutine in the inherited from the immediate parent coroutine, but only until the first suspension point. After suspension, it resumes the coroutine in the other thread.

Example.

runBlocking {
launch(Dispatchers.Unconfined) {
Log.i(“Dispatcher”, “Before suspension Thread
${Thread.currentThread().name}”)
delay(300L)
Log.i(Dispatcher, “After suspension Thread : ${Thread.currentThread().name}”)
}
}

Output:

Dispatcher: Before suspension Thread: main

Dispatcher: After suspension Thread: kotlinx.coroutines.DefaultExecutor

If we execute the above code, We can see that Before suspension one is running on the main thread. However we are delaying for 300 milliseconds, and After the suspension is actually running a different thread.

Takeaway / Conclusion:
With this blog, you got to know about different dispatchers & scopes. So now you can use them as per your use case. Scopes help to manage the lifetime of coroutines and the major benefit of this is to avoid memory leaks. Dispatcher will help to decide in which thread the coroutine runs. Accordingly, in our task we can provide a specific thread like for network operation and assign IO thread.

Credit Tags
https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/

https://developer.android.com/topic/libraries/architecture/coroutines

--

--