Preamble
This article is for anyone who is curious about Coroutines in Kotlin but doesn’t know exactly what it is.
In this tutorial, we will learn Coroutines in Kotlin with the following topics:
- What exactly is Coroutines?
- Why is the solution needed by Kotlin Coroutines?
- Step by step guide on how to deploy Kotlin Coroutines in Android.
- What is the scope of Corlinines?
- Exception handling in Kotlin Coroutines.
Reference source: https://blog.mindorks.com/mastering-kotlin-coroutines-in-android-step-by-step-guide
What is coroutines?
Coroutines = Co + Routines Here, Co means collaboration and Routines means functions. Meaning that when the functions work together, we call it Coroutines.
See the example below to understand more about this. Suppose we have 2 functions, functionA and functionB .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <span class="token keyword">fun</span> <span class="token function">functionA</span> <span class="token punctuation">(</span> case <span class="token operator">:</span> Int <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">when</span> <span class="token punctuation">(</span> case <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token number">1</span> <span class="token operator">-></span> <span class="token punctuation">{</span> <span class="token function">taskA1</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token function">functionB</span> <span class="token punctuation">(</span> <span class="token number">1</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token number">2</span> <span class="token operator">-></span> <span class="token punctuation">{</span> <span class="token function">taskA2</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token function">functionB</span> <span class="token punctuation">(</span> <span class="token number">2</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token number">3</span> <span class="token operator">-></span> <span class="token punctuation">{</span> <span class="token function">taskA3</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token function">functionB</span> <span class="token punctuation">(</span> <span class="token number">3</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token number">4</span> <span class="token operator">-></span> <span class="token punctuation">{</span> <span class="token function">taskA4</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token function">functionB</span> <span class="token punctuation">(</span> <span class="token number">4</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
functionB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <span class="token keyword">fun</span> <span class="token function">functionB</span> <span class="token punctuation">(</span> case <span class="token operator">:</span> Int <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">when</span> <span class="token punctuation">(</span> case <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token number">1</span> <span class="token operator">-></span> <span class="token punctuation">{</span> <span class="token function">taskB1</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token function">functionA</span> <span class="token punctuation">(</span> <span class="token number">2</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token number">2</span> <span class="token operator">-></span> <span class="token punctuation">{</span> <span class="token function">taskB2</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token function">functionA</span> <span class="token punctuation">(</span> <span class="token number">3</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token number">3</span> <span class="token operator">-></span> <span class="token punctuation">{</span> <span class="token function">taskB3</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token function">functionA</span> <span class="token punctuation">(</span> <span class="token number">4</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token number">4</span> <span class="token operator">-></span> <span class="token punctuation">{</span> <span class="token function">taskB4</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Then we can call functionA as follows:
1 2 | <span class="token function">functionA</span> <span class="token punctuation">(</span> <span class="token number">1</span> <span class="token punctuation">)</span> |
Here, functionA will execute taskA1 and then give control to functionB to execute taskB1 .
Then functionB will execute taskB1 and return control to functionA to execute taskA2 , etc.
This means that functionA and functionB are working together.
With Kotlin Coroutines, this cooperation can be done very easily without using the when or switch case I used in the above example.
Now, we understand what coroutines are when it comes to collaboration between functions. There are endless possibilities that open because of the collaborative nature of the functions.
Some possibilities are as follows:
- It can execute a few lines of functionA and then execute a few lines of functionB and then again several lines of functionA , etc. This will be useful when a thread is standing still doing nothing, in which case, it can execute a few lines of another function. In this way, it is possible to take full advantage of the thread . Finally, this collaboration helps in multitasking.
- It will allow writing asynchronous code in a synchronous way. We will talk about this later in this article.
Overall, Coroutines makes multitasking very easy.
So we can say that both Coroutines and threads are multitasking. But the difference is that threads are managed by the operating system and coroutines by the user because it can execute several functional lines by leveraging collaboration.
It’s an optimization framework written on the actual thread by taking advantage of the collaborative nature of the functions to make it lighter and more powerful. So we can say that Coroutines are lightweight threads. A lightweight thread means that it does not map on the native thread. Therefore, it does not require context switching on the processor, so they are faster.
What does “it doesn’t map on a native thread” mean?
Coroutines is available in many languages. Basically, there are two types of Coroutines:
- Stacking (Stackful)
- No stacking (Stackless)
Kotlin implements stackless coroutines – that means coroutines don’t have their own stack, so it doesn’t map on the native thread. Now, you can understand the paragraph below, the official Kotlin website says
One can think of a coroutine as a light-weight thread. Like threads, coroutines can run in parallel, wait for each other and communicate. The biggest difference is that coroutines are very cheap, almost free: we can create thousands of them, and pay very little in terms of performance. True threads, on the other hand, are expensive to start and keep around. A thousand threads can be a serious challenge for a modern machine.
Coroutines do not replace threads, it is like a framework to manage them.
Correct definition of Coroutines: A framework for concurrent management in a simpler and more efficient way with lightweight threads written on top of actual threading framework to make the most of it using the collaborative nature of functions. . We now understand exactly what Coroutines are. So we need to know why we need the solutions that Coroutines Kotlin provides.
Why do you need Kotlin Coroutines?
Let’s take a standard use case of Android Application as follows:
- Get the User from the server.
- Display User on UI
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token keyword">fun</span> <span class="token function">fetchUser</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">:</span> User <span class="token punctuation">{</span> <span class="token comment">// make network call</span> <span class="token comment">// return user</span> <span class="token punctuation">}</span> <span class="token keyword">fun</span> <span class="token function">showUser</span> <span class="token punctuation">(</span> user <span class="token operator">:</span> User <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// show user</span> <span class="token punctuation">}</span> <span class="token keyword">fun</span> <span class="token function">fetchAndShowUser</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">val</span> user <span class="token operator">=</span> <span class="token function">fetchUser</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token function">showUser</span> <span class="token punctuation">(</span> user <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
When we call fetchAndShowUser function, it will throw to NetworkOnMainThreadException because network call is not allowed on main thread. There are many ways to solve that. Some of them are as follows:
- Using Callback : Here, we run fetchUser in the background thread and pass the result to the callback.
1 2 3 4 5 6 | <span class="token keyword">fun</span> <span class="token function">fetchAndShowUser</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> fetchUser <span class="token punctuation">{</span> user <span class="token operator">-></span> <span class="token function">showUser</span> <span class="token punctuation">(</span> user <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- Using RxJava: This way we can escape the nested callback.
1 2 3 4 5 6 7 | <span class="token function">fetchUser</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">subscribeOn</span> <span class="token punctuation">(</span> Schedulers <span class="token punctuation">.</span> <span class="token function">io</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">observerOn</span> <span class="token punctuation">(</span> AndroidSchedulers <span class="token punctuation">.</span> <span class="token function">mainThread</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">subscribe</span> <span class="token punctuation">{</span> user <span class="token operator">-></span> <span class="token function">showUser</span> <span class="token punctuation">(</span> user <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
- Using Coroutines:
1 2 3 4 5 | <span class="token keyword">suspend</span> <span class="token keyword">fun</span> <span class="token function">fetchAndShowUser</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">val</span> user <span class="token operator">=</span> <span class="token function">fetchUser</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token comment">// fetch on IO thread</span> <span class="token function">showUser</span> <span class="token punctuation">(</span> user <span class="token punctuation">)</span> <span class="token comment">// back on UI thread</span> <span class="token punctuation">}</span> |
The above code looks to be synchronized, but not really synchronized. We will find out
Deploy Kotlin Coroutines in Android
Add Kotlin Coroutines dependencies to Android project as shown below:
1 2 3 4 5 | dependencies <span class="token punctuation">{</span> implementation <span class="token string">"org.jetbrains.kotlinx:kotlinx-coroutines-core:x.x.x"</span> implementation <span class="token string">"org.jetbrains.kotlinx:kotlinx-coroutines-android:x.x.x"</span> <span class="token punctuation">}</span> |
Now, the fetchUser function will look like below:
1 2 3 4 5 6 7 | <span class="token keyword">suspend</span> <span class="token keyword">fun</span> <span class="token function">fetchUser</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">:</span> User <span class="token punctuation">{</span> <span class="token keyword">return</span> GlobalScope <span class="token punctuation">.</span> <span class="token function">async</span> <span class="token punctuation">(</span> Dispatchers <span class="token punctuation">.</span> IO <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// make network call</span> <span class="token comment">// return user</span> <span class="token punctuation">}</span> <span class="token punctuation">.</span> <span class="token function">await</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
And the fetchAndShowUser function will look like this:
1 2 3 4 5 | <span class="token keyword">suspend</span> <span class="token keyword">fun</span> <span class="token function">fetchAndShowUser</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">val</span> user <span class="token operator">=</span> <span class="token function">fetchUser</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token comment">// fetch on IO thread</span> <span class="token function">showUser</span> <span class="token punctuation">(</span> user <span class="token punctuation">)</span> <span class="token comment">// back on UI thread</span> <span class="token punctuation">}</span> |
And the function showUser below remains the same:
1 2 3 4 | <span class="token keyword">fun</span> <span class="token function">showUser</span> <span class="token punctuation">(</span> user <span class="token operator">:</span> User <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// show user</span> <span class="token punctuation">}</span> |
Inside:
- Dispatchers : Dispatchers help coroutines decide which thread the job is to do. There are three main types of Dispatchers: IO , Default and Main . IO dispatcher is used to perform network and disk-related tasks. Default is used to do CPU intensive work. Main is the UI thread of Android. To use them, we need to wrap the work under the async function. The Async function looks like below.
1 2 | <span class="token keyword">suspend</span> <span class="token keyword">fun</span> <span class="token function">async</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token comment">// implementation removed for brevity</span> |
- suspend : The suspend function is a function that can start, pause and resume.
Suspend functions are only allowed to be called from another coroutine or other suspend function. You can see that the async function includes the keyword suspend . Therefore, to use it, we also need to implement the suspend function.
So fetchAndShowUser can only be called from another suspend function or coroutine. We cannot perform the onCreate of an suspend activity, so we need to call it from the coroutines as shown below:
1 2 3 4 5 6 7 8 9 10 | <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onCreate</span> <span class="token punctuation">(</span> savedInstanceState <span class="token operator">:</span> Bundle <span class="token operator">?</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span> <span class="token punctuation">.</span> <span class="token function">onCreate</span> <span class="token punctuation">(</span> savedInstanceState <span class="token punctuation">)</span> GlobalScope <span class="token punctuation">.</span> <span class="token function">launch</span> <span class="token punctuation">(</span> Dispatchers <span class="token punctuation">.</span> Main <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">val</span> user <span class="token operator">=</span> <span class="token function">fetchUser</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token comment">// fetch on IO thread</span> <span class="token function">showUser</span> <span class="token punctuation">(</span> user <span class="token punctuation">)</span> <span class="token comment">// back on UI thread</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
showUser will run on the UI thread because we used Dispatchers.Main to launch it.
There are two functions in Kotlin to start coroutines as follows:
launch{}
async{}
Launch and Async in Kotlin Coroutines
The difference is that launch{}
doesn’t return anything and async{}
returns a Deferred<T>
, where the await()
function returns the coroutine result as we have the future in Java, executing the future. get () to get the result.
Another way to say:
launch
: Done then forgottenasync
: Perform a task and return a result
Take an example to learn Launch and Async.
We have a fetchUserAndSaveInDatabase function as shown below:
1 2 3 4 5 6 | <span class="token keyword">suspend</span> <span class="token keyword">fun</span> <span class="token function">fetchUserAndSaveInDatabase</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// fetch user from network</span> <span class="token comment">// save user in database</span> <span class="token comment">// and do not return anything</span> <span class="token punctuation">}</span> |
Now, we can use launch as below:
1 2 3 4 | GlobalScope <span class="token punctuation">.</span> <span class="token function">launch</span> <span class="token punctuation">(</span> Dispatchers <span class="token punctuation">.</span> Main <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">fetchUserAndSaveInDatabase</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token comment">// do on IO thread</span> <span class="token punctuation">}</span> |
Because fetchUserAndSaveInDatabase doesn’t return anything, we can use launch to complete the task and then do something on the Main Thread.
But when we need results back, we need to use async .
We have two functions that return the User as below:
1 2 3 4 5 | <span class="token keyword">suspend</span> <span class="token keyword">fun</span> <span class="token function">fetchFirstUser</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">:</span> User <span class="token punctuation">{</span> <span class="token comment">// make network call</span> <span class="token comment">// return user</span> <span class="token punctuation">}</span> |
1 2 3 4 5 | <span class="token keyword">suspend</span> <span class="token keyword">fun</span> <span class="token function">fetchSecondUser</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">:</span> User <span class="token punctuation">{</span> <span class="token comment">// make network call</span> <span class="token comment">// return user</span> <span class="token punctuation">}</span> |
Now, we can use async as shown below:
1 2 3 4 5 6 | GlobalScope <span class="token punctuation">.</span> <span class="token function">launch</span> <span class="token punctuation">(</span> Dispatchers <span class="token punctuation">.</span> Main <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">val</span> userOne <span class="token operator">=</span> <span class="token function">async</span> <span class="token punctuation">(</span> Dispatchers <span class="token punctuation">.</span> IO <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">fetchFirstUser</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">val</span> userTwo <span class="token operator">=</span> <span class="token function">async</span> <span class="token punctuation">(</span> Dispatchers <span class="token punctuation">.</span> IO <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">fetchSecondUser</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function">showUsers</span> <span class="token punctuation">(</span> userOne <span class="token punctuation">.</span> <span class="token function">await</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> userTwo <span class="token punctuation">.</span> <span class="token function">await</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token comment">// back on UI thread</span> <span class="token punctuation">}</span> |
Here, it makes both network calls parallel, waits for the results, and then calls the showUsers function.
We now understand the difference between Launch and Async.
Next we will talk about withContext .
1 2 3 4 5 6 7 | <span class="token keyword">suspend</span> <span class="token keyword">fun</span> <span class="token function">fetchUser</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">:</span> User <span class="token punctuation">{</span> <span class="token keyword">return</span> GlobalScope <span class="token punctuation">.</span> <span class="token function">async</span> <span class="token punctuation">(</span> Dispatchers <span class="token punctuation">.</span> IO <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// make network call</span> <span class="token comment">// return user</span> <span class="token punctuation">}</span> <span class="token punctuation">.</span> <span class="token function">await</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
withContext is nothing more than an async way of writing that we don’t have to write await () .
1 2 3 4 5 6 7 | <span class="token keyword">suspend</span> <span class="token keyword">fun</span> <span class="token function">fetchUser</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">:</span> User <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token function">withContext</span> <span class="token punctuation">(</span> Dispatchers <span class="token punctuation">.</span> IO <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// make network call</span> <span class="token comment">// return user</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
But there are many more things we should know about withContext and await.
Now, let’s use withContext in our async example of fetchFirstUser and Now, let’s use withContext in our async example of fetchFirstUser and fetchSecondUser in parallel.
1 2 3 4 5 6 | GlobalScope <span class="token punctuation">.</span> <span class="token function">launch</span> <span class="token punctuation">(</span> Dispatchers <span class="token punctuation">.</span> Main <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">val</span> userOne <span class="token operator">=</span> <span class="token function">withContext</span> <span class="token punctuation">(</span> Dispatchers <span class="token punctuation">.</span> IO <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">fetchFirstUser</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">val</span> userTwo <span class="token operator">=</span> <span class="token function">withContext</span> <span class="token punctuation">(</span> Dispatchers <span class="token punctuation">.</span> IO <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">fetchSecondUser</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function">showUsers</span> <span class="token punctuation">(</span> userOne <span class="token punctuation">,</span> userTwo <span class="token punctuation">)</span> <span class="token comment">// back on UI thread</span> <span class="token punctuation">}</span> |
When we use withContext , it will run in sequence instead of in parallel. That is a big difference.
Rule:
- Use withContext when you don’t need to execute it in parallel.
- Only use async when you need to execute in parallel.
- Both withContext and async can be used to get results, not with launch .
- Use withContext to return the result of a task.
- Using async results from multiple tasks running in parallel.
Scope of Kotlin Coroutines
Scope in Kotlin Coroutines is very useful because we need to cancel the background task as soon as the activity is canceled. Here, we will learn how to use scope to handle these types of situations.
Assuming our activity is scope, the background task will be canceled as soon as the activity is canceled.
In the activity, we need to implement CoroutineScope.
1 2 3 4 5 6 7 8 9 | class MainActivity : AppCompatActivity(), CoroutineScope { override val coroutineContext: CoroutineContext get() = Dispatchers.Main + job private lateinit var job: Job } |
In the onCreate and onDestroy function.
1 2 3 4 5 6 7 8 9 10 | <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onCreate</span> <span class="token punctuation">(</span> savedInstanceState <span class="token operator">:</span> Bundle <span class="token operator">?</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span> <span class="token punctuation">.</span> <span class="token function">onCreate</span> <span class="token punctuation">(</span> savedInstanceState <span class="token punctuation">)</span> job <span class="token operator">=</span> <span class="token function">Job</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token comment">// create the Job</span> <span class="token punctuation">}</span> <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onDestroy</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> job <span class="token punctuation">.</span> <span class="token function">cancel</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token comment">// cancel the Job</span> <span class="token keyword">super</span> <span class="token punctuation">.</span> <span class="token function">onDestroy</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
For now, just use the launch like below:
1 2 3 4 5 6 | launch <span class="token punctuation">{</span> <span class="token keyword">val</span> userOne <span class="token operator">=</span> <span class="token function">async</span> <span class="token punctuation">(</span> Dispatchers <span class="token punctuation">.</span> IO <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">fetchFirstUser</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">val</span> userTwo <span class="token operator">=</span> <span class="token function">async</span> <span class="token punctuation">(</span> Dispatchers <span class="token punctuation">.</span> IO <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">fetchSecondUser</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function">showUsers</span> <span class="token punctuation">(</span> userOne <span class="token punctuation">.</span> <span class="token function">await</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> userTwo <span class="token punctuation">.</span> <span class="token function">await</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
As soon as the activity is canceled, the task will be canceled if it is running because we defined the scope.
When we need global scope is our application scope, not the activity scope, we can use GlobalScope as follows:
1 2 3 4 5 | GlobalScope <span class="token punctuation">.</span> <span class="token function">launch</span> <span class="token punctuation">(</span> Dispatchers <span class="token punctuation">.</span> Main <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">val</span> userOne <span class="token operator">=</span> <span class="token function">async</span> <span class="token punctuation">(</span> Dispatchers <span class="token punctuation">.</span> IO <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">fetchFirstUser</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">val</span> userTwo <span class="token operator">=</span> <span class="token function">async</span> <span class="token punctuation">(</span> Dispatchers <span class="token punctuation">.</span> IO <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">fetchSecondUser</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Here, even if the operation is canceled, the fetchUser functions will continue to run because we have used GlobalScope .
This is how Scopes in Kotlin Coroutines become useful.
Exception handling in Kotlin Coroutines.
When using launch
One way is to use try-Catch:
1 2 3 4 5 6 7 8 | GlobalScope <span class="token punctuation">.</span> <span class="token function">launch</span> <span class="token punctuation">(</span> Dispatchers <span class="token punctuation">.</span> Main <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token function">fetchUserAndSaveInDatabase</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token comment">// do on IO thread and back to UI Thread</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span> exception <span class="token operator">:</span> Exception <span class="token punctuation">)</span> <span class="token punctuation">{</span> Log <span class="token punctuation">.</span> <span class="token function">d</span> <span class="token punctuation">(</span> TAG <span class="token punctuation">,</span> <span class="token string">" <span class="token interpolation variable">$exception</span> handled !"</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
The alternative is to use a handler
To do so we need to initialize an exception handler as follows:
1 2 3 4 | <span class="token keyword">val</span> handler <span class="token operator">=</span> CoroutineExceptionHandler <span class="token punctuation">{</span> _ <span class="token punctuation">,</span> exception <span class="token operator">-></span> Log <span class="token punctuation">.</span> <span class="token function">d</span> <span class="token punctuation">(</span> TAG <span class="token punctuation">,</span> <span class="token string">" <span class="token interpolation variable">$exception</span> handled !"</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Then attach this handler as follows
1 2 3 4 | GlobalScope <span class="token punctuation">.</span> <span class="token function">launch</span> <span class="token punctuation">(</span> Dispatchers <span class="token punctuation">.</span> Main <span class="token operator">+</span> handler <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">fetchUserAndSaveInDatabase</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token comment">// do on IO thread and back to UI Thread</span> <span class="token punctuation">}</span> |
If there is an exception in fetchUserAndSaveInDatabase , it will be handled in the handler we just attached.
When using activity scope, we can attach exception to coroutineContext
as follows:
1 2 3 4 5 6 7 8 9 | <span class="token keyword">class</span> MainActivity <span class="token operator">:</span> <span class="token function">AppCompatActivity</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> CoroutineScope <span class="token punctuation">{</span> <span class="token keyword">override</span> <span class="token keyword">val</span> coroutineContext <span class="token operator">:</span> CoroutineContext <span class="token keyword">get</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">=</span> Dispatchers <span class="token punctuation">.</span> Main <span class="token operator">+</span> job <span class="token operator">+</span> handler <span class="token keyword">private</span> <span class="token keyword">lateinit</span> <span class="token keyword">var</span> job <span class="token operator">:</span> Job <span class="token punctuation">}</span> |
and use:
1 2 3 4 | launch <span class="token punctuation">{</span> <span class="token function">fetchUserAndSaveInDatabase</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
When Using async
When using async, we need to use the try-catch block to handle the exception below.
1 2 3 4 5 6 7 8 9 | <span class="token keyword">val</span> deferredUser <span class="token operator">=</span> GlobalScope <span class="token punctuation">.</span> <span class="token function">async</span> <span class="token punctuation">{</span> <span class="token function">fetchUser</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token keyword">val</span> user <span class="token operator">=</span> deferredUser <span class="token punctuation">.</span> <span class="token function">await</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span> exception <span class="token operator">:</span> Exception <span class="token punctuation">)</span> <span class="token punctuation">{</span> Log <span class="token punctuation">.</span> <span class="token function">d</span> <span class="token punctuation">(</span> TAG <span class="token punctuation">,</span> <span class="token string">" <span class="token interpolation variable">$exception</span> handled !"</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Now, let’s look at some more practical use cases of exception handling in Android Development.
Let’s say we have two network calls as below:
getUsers()
getMoreUsers()
And, we are making network calls in sequence as below:
1 2 3 4 5 6 7 8 9 | launch <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token keyword">val</span> users <span class="token operator">=</span> <span class="token function">getUsers</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">val</span> moreUsers <span class="token operator">=</span> <span class="token function">getMoreUsers</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span> exception <span class="token operator">:</span> Exception <span class="token punctuation">)</span> <span class="token punctuation">{</span> Log <span class="token punctuation">.</span> <span class="token function">d</span> <span class="token punctuation">(</span> TAG <span class="token punctuation">,</span> <span class="token string">" <span class="token interpolation variable">$exception</span> handled !"</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
If one of the network calls fails, it will go directly to the catch block.
But suppose we want to return an empty list for a failed network call and continue with the response from another network call. We can add try-catch
blocks to individual network calls as below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | launch <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token keyword">val</span> users <span class="token operator">=</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token function">getUsers</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span> e <span class="token operator">:</span> Exception <span class="token punctuation">)</span> <span class="token punctuation">{</span> emptyList <span class="token operator"><</span> User <span class="token operator">></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">val</span> moreUsers <span class="token operator">=</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token function">getMoreUsers</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span> e <span class="token operator">:</span> Exception <span class="token punctuation">)</span> <span class="token punctuation">{</span> emptyList <span class="token operator"><</span> User <span class="token operator">></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span> exception <span class="token operator">:</span> Exception <span class="token punctuation">)</span> <span class="token punctuation">{</span> Log <span class="token punctuation">.</span> <span class="token function">d</span> <span class="token punctuation">(</span> TAG <span class="token punctuation">,</span> <span class="token string">" <span class="token interpolation variable">$exception</span> handled !"</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
This way, if an error occurs, it will continue with the empty list.
Now, if we want to make network calls in parallel. We can write code like below using async
.
1 2 3 4 5 6 7 8 9 10 11 | launch { try { val usersDeferred = async { getUsers() } val moreUsersDeferred = async { getMoreUsers() } val users = usersDeferred.await() val moreUsers = moreUsersDeferred.await() } catch (exception: Exception) { Log.d(TAG, "$exception handled !") } } |
Here, we will face a problem, if the network error returns, the app will crash! , it will not be able to reach the catch
block.
To solve this problem, we will use coroutineScope
as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 | launch <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> coroutineScope <span class="token punctuation">{</span> <span class="token keyword">val</span> usersDeferred <span class="token operator">=</span> async <span class="token punctuation">{</span> <span class="token function">getUsers</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">val</span> moreUsersDeferred <span class="token operator">=</span> async <span class="token punctuation">{</span> <span class="token function">getMoreUsers</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">val</span> users <span class="token operator">=</span> usersDeferred <span class="token punctuation">.</span> <span class="token function">await</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">val</span> moreUsers <span class="token operator">=</span> moreUsersDeferred <span class="token punctuation">.</span> <span class="token function">await</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span> exception <span class="token operator">:</span> Exception <span class="token punctuation">)</span> <span class="token punctuation">{</span> Log <span class="token punctuation">.</span> <span class="token function">d</span> <span class="token punctuation">(</span> TAG <span class="token punctuation">,</span> <span class="token string">" <span class="token interpolation variable">$exception</span> handled !"</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Now no matter which network error it occurs, it will reach the catch
block.
As suppose again, we want to return an empty list for the failed network call and continue waiting for the response from the other network call. We will need to use the supervisorScope
and add try-catch
blocks to each individual network call as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | launch { try { supervisorScope { val usersDeferred = async { getUsers() } val moreUsersDeferred = async { getMoreUsers() } val users = try { usersDeferred.await() } catch (e: Exception) { emptyList<User>() } val moreUsers = try { moreUsersDeferred.await() } catch (e: Exception) { emptyList<User>() } } } catch (exception: Exception) { Log.d(TAG, "$exception handled !") } } |
Thus, this way even if an error occurs, it will continue with an empty list.
This is how the supervisorScope
works.
Conclude
- When Not using
async
, we can still continue withtry-catch
orCoroutineExceptionHandler
and achieve whatever you want with each case. - When using
async
, in addition totry-catch
, we still have 2 more optionscoroutineScope
andsupervisorScope
- With
async
using thesupervisorScope
with each individualtry-catch
of each task, within the outertry-catch
block, you can continue other tasks even if one of them fails. - For
async
, usecoroutineScope
with the outertry-catch
block, when you don’t want to continue with other tasks when one of them fails.
This is an example git of Coroutines author. Please refer to it
https://github.com/MindorksOpenSource/Kotlin-Coroutines-Android-Examples