Fast Lane to Coroutines

Bhoomi Shah
Mindful Engineering
8 min readJan 22, 2020
Understanding the basics of Coroutines family

Kotlin has become one of the immense fields with its several features, Making it one of the most loved languages among developers and making their lives easier.

One of the features we are going to dive into is Coroutines.

Coroutines: Introduced in Kotlin version 1.3, is the new way to write asynchronous callbacks inside an application. We write asynchronous callbacks when we make a network request for fetching data, database operations or loading images, such long-running operations must be performed in different threads without blocking the main thread.

The very first question arises to our mind is why we use Coroutines if we already have a Threads, AsyncTasks, and RxJava to write our asynchronous code?

Coroutines are “lightweight threads”. Which manages background threads that can simplify code by reducing the need for callbacks, And converts the long-running tasks, such as a database or network access into sequential code.

Key Concepts:

  • Suspend functions
  • Coroutine context and Coroutine builders
  • Coroutine Scope

How it is different from Threads?

Threads can be problematic if we use multiple of them inside our application, it can be fatal to the performance. Nonetheless, it can give overheads to the garbage collector.

Whereas several coroutines can be executed using the same thread, So while the number of threads that we can run in an Application is limited, we can run as many coroutines as we need. The limit is practically infinite.

Removing the need for callback pattern by using coroutines.

At times we need to make several network requests in a sequential manner such as on callback of the first request we perform a second network request.

i.e. Once we are done calling Login API, In its callback, we are triggering favorite list API and finally, we show favorite items on UI.

But the callback pattern has some disadvantages such as the code can become hard to read if we add one more request to the callback such as fetching purchased items list. Exception handling can also become a serious concern.

Therefore coroutines are a great replacement for callback pattern and it makes code much more readable and concise by writing sequentially.

Let’s see how

Suspend functions

Suspending functions block the execution of coroutine while they are doing work and once they finish, the result of a task is returned and used in the next line similar to callback pattern.

An important thing to note is suspend function can only be called/run from another suspend function or inside coroutine block.

Due to its sequential style, suspend code can reduce the use of callbacks compared to callback-based code.

To define a suspend function we need to write the keyword suspend , it’s Kotlin’s way of marking a function, available to coroutines.

From the above code, you might wonder on which thread your code will run?

As we already have two background tasks running, fetching user details and its favorite items. With the above tasks we also need to show a progress bar and later we display the result on UI, these UI related tasks must be done on Main thread else your App will crash.

The answer is, your code will run on the thread depend on which coroutine context you provide to your functions. Let’s see more about the coroutine context!

Coroutine context

Coroutine context tells the coroutine on which thread to execute and how it is defined. It contains a set of various elements, but Dispatchers and Job are one of its main elements.

Dispatchers

As we know dispatchers are contexts, that define on which thread the coroutine will run, it will optimize the process by reusing threads using a pool of threads. The system will try to reuse the threads that are already active.

There are three main dispatchers we use:

Dispatchers.Default

When no dispatchers are defined, the default dispatcher is used. We can set it explicitly by using for the tasks which have CPU intensive work, Arithmetic operations, App computations such as Sorting or Parsing the listings.

Dispatchers.IO

We use this mainly for disk operations such as network requests, database reading/writing operations, downloading files. As most of the work will be waiting, so no CPU intensive work will be taken place, hence one can run several tasks parallelly too. We use this dispatcher most of the time.

Dispatchers.Main

Special kind of dispatcher, we use it for accessing main thread, hence any UI related work will be written inside coroutine using this dispatcher. So now you know where to write UI related (progress.visibility = View.VISIBLE) code.

Above we have discussed dispatchers which can be used in two ways, explicitly and withContext function. Now, withContext mainly used when we want to change the context inside coroutine that has already defined its context.

As shown in the below example, here the function already contains the dispatcher defined as Dispatchers.Main to show a progress bar, but you need to change the thread for your network request userLogin() as Dispatchers.IO, we can easily change the context for a specific function by allowing it to write inside the withContext(Dispatchers.IO).

Now that we know on which thread our code will execute, now where to write the execution of coroutines?

Coroutine Builders

As the name says Coroutine builders are the functions in which you build your coroutine code and manage its execution.

Builders are the extension functions for your Coroutine Scope. Depending on the requirements we can use different coroutine builders, and we can make one too.

runBlocking

Blocks the current thread and waits until it finishes its execution. The tasks inside the runBlocking builder will execute sequentially and start only when its above tasks are completed, hence it keeps blocking the thread until it finishes all its tasks written inside the function.

So why to use runBlocking then? It’s used for testing your suspending functions. The example below itself is self-explanatory.

Launch

We use asynchronous functions to not block the current thread and work all its tasks parallelly, that is the whole point of using coroutines right? So unlike runBlocking, the launch will not block its current thread depending upon the dispatchers we use.

The simplest way to use coroutines is by using launch, the tasks written inside will simply execute in the background. Launch returns a Job, which can be used to perform cancel or join while performing executions.

Output will be:
//Launch started
//Launch general details
//Launch inside
//Launch completed

In the above code we are using launch builder in coroutine scope, “Launch started” will be printed first. Now as we know the launch will work asynchronously so it will call userLogin() and getFavoriteItemList() functions on the background thread.

Now irrespective of the result is returned or not, the generalDetails() launch builder will get called by printing value “Launch general details”.

Hence after some time the “Launch inside” will get printed on your console when the favorite items are fetched and finally moves to the line “Launch completed”.

Async await()

Several tasks can run parallel using the async function as we know, but it’s different from other functions because it returns the Deferred object.

await() is the function of the Deferred object and can be called only when we need the result. Note that async is not a suspending function itself hence it should be called inside the coroutine.

Let’s understand the async await builder by below example

In the above example, we are first fetching the user object, using which we need to fetch the favorite and purchased items of the user. These tasks need to be called simultaneously to reduce the execution time as they are independent of each other.

Using a launch builder can take more time to finish tasks as it will work sequentially. Whereas the async builder can execute tasks parallelly.

Whenever we need the result of async task, we call await(). It checks that the result is ready If so it will return the result and continue to display the items on the UI. If not then the coroutine will suspend and waits until the result is delivered, but it will not block the thread.

Scopes

Each asynchronous operation runs within a particular scope. Simply scopes are used inside which we write coroutines. When the scope is destroyed the coroutines inside it will be canceled.

Hence scopes are important within which we tell our coroutine to execute, and while destroying activity or applications the background tasks will be stopped when the activity is destroyed.

Coroutine builder is an extension on CoroutineScope and it inherits its coroutineContext.

We have different scopes let’s see each of them below:

Global Scope

As its name says global scope is used whenever the coroutines are to be executed within the lifecycle of application class as a global level.

The coroutines written inside the global scope will get canceled or destroyed only when the application is destroyed.

GlobalScope.launch(Dispatchers.Main) {
....
}

ViewModelScope

A ViewModelScope is defined for each ViewModel that is in your app. Any coroutine executed within this will be canceled once the ViewModel is destroyed. Used when we want our job done only when ViewModel is in an active state.

For ViewModelScope, use androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0-beta01 or higher

As shown below, the viewModelScope is used inside the ViewModel Here launch is our coroutine builder, inside which we execute the background task with coroutine context as background thread (Dispatchers.IO).

viewModelScope.launch(Dispatchers.IO) {
....

}

Implementing CoroutineScope

One more way to execute your coroutine inside the scope while activity/fragment is active by implementing this interface, which overrides the coroutineContext property.

We provide the coroutine context which we need to use for running the coroutine inside. Here coroutine context we use is Dispatchers.Main.

class MainActivity : AppCompatActivity(), CoroutineScope {override val coroutineContext: CoroutineContext
get() = Dispatchers.Main
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
launch {
...
}
}
}

So that’s how we can say that Coroutines are a futuristic way to execute your background tasks compared to traditional techniques like AsyncTasks, Threads, etc..

Now start using Coroutines. Once you do I am sure you’ll love it! Feel free to share your views in the comment section below.

Happy Coding!

--

--

Bhoomi Shah
Mindful Engineering

Senior Android Developer | Computer Science Student| #Android #Kotlin #Java.