If you have not read part one of this series, please read it first, it explains the main idea behind Kotlin coroutines.
If you read the part one of this series, you may notice that error handling was not covered as a topic at all, so in this post, we will go through techniques you can utilize to handle errors that may occur inside coroutines.
The main idea introduced in part one of this series is the idea of async code written as a synchronous code and if you think about it, how would you handle errors in a sync style code, you catch exceptions. Same idea here, we can surround code that throws an error inside try/catch block, and handle error states in your async code. Let’s see an example of a retrofit network call.
As we can see in the example above, we surrounded the retrofit method execute with try and catch blocks, and two exceptions are caught that execute method can throw, IOException and RuntimeException. Why just these two exceptions? If you look at the documentation for execute method, you can see that execute method can throw only those two exceptions. In case an exception is thrown, the user will be notified, and that part of the code runs on the main thread.
A better way would be to pass in an exception handler that will be called once the launch coroutine encounters and exception.
Async vs launch builder
In part 1, we mainly used launch coroutine builder as our main coroutine block, and one problem with launch builder is the exception handling, if an exception is not caught inside a coroutine, the whole app will crash. Kotlin provides another coroutine builder, Async builder, which behaves differently in few ways. One way in which it differs is exception handling, async block just ignores thrown exceptions if they are not caught resulting in a non app crash scenario. Here is a way to catch an exception with async block
What if you need to cancel a coroutine, for example, the android activity gets paused or destroyed, and the coroutine result is no longer needed?
All coroutine builders return a job, which can be canceled at any point by calling job.cancel(). What is interesting with Kotlin coroutines is the ability to specify a parent job as a context for multiple coroutines, and calling cancel() on the parent coroutine will result in all coroutines being canceled, very convinient. In all examples above you may notice the parentJob passed in in every coroutine, and calling parentJob.cancel() inside onPause() activity method results in all coroutines being cancelled.
Hooking into callback code
The last thing I want to talk about is hooking into code that is async but uses callbacks to handle the suspension of the flow. For example, firebase database uses callbacks to inform the caller an operation has completed. In that case, you need to bridge between a Kotlin coroutine and a callback listener. SuspendCoroutine to the rescue! A special coroutine which will be suspended by default, and you call resume on it inside the callback method. Below is an example from one of my apps that uses google firebase as a backend.
This getBooleanValue can be used inside any coroutine and will behave as any other suspend function, hence creating a bridge between Kotlin coroutine and callback.
I have created a sample app that uses all exception handling cases shown in this blog post on this Github repository.
If you liked the article share it if you loved it comment below :).