Coroutines

Exception handling in coroutines

Pinterest LinkedIn Tumblr

In this post, we will learn exception handling in coroutines. Later on, we will know how to handle exceptions in a sequential program.

In coroutine, exception behavior depends on coroutines builder. So we already know that we have to two coroutines builders. We have launch and async. launch generates a job and async will generate a deferred that will give us a value. So based on that the behavior changes slightly.

Exception behaviour with launch

So for launch, As you know launch creates a job hierarchy. So exceptions will propagate instantly through the parent-child hierarchy and will immediately cause jobs to fail. So there is no delay and no way to catch it automatically. So what we need to do, we need to use try-catch inside the launch or we have a structure called an exception handler. So the summary of this is below

  • Propagates through the parent-child hierarchy
  • The exception will be thrown immediately and jobs will fail
  • use try-catch or an exception handler

In sort, launch automatically generates the exception and causes all the jobs to fail in the exception hierarchy. In coroutines, you won’t see the exceptions straight away. you will see the exception when you try to actually use the job or coroutines in the main thread. So if you call join on the job you will automatically join into main thread and then you will see the exception.

Behaviour with async

The async is slightly different. So async means we don’t have a hierarchy we just have the outcome. So the exception is deferred, and it will not be shown until the result is consumed. So if we call wait then we will see the exception. If we never call wait we will never see the exception. So what we need to do in this case is use the try-catch when we are calling await call. In sort we can say

  • Exceptions are deferred until the result is consumed
  • If the result is not consumed, the exception is never thrown
  • try-catch in the coroutine or in the await() call

So the difference is the launch propagated the exception through the job hierarchy immediately and kill the jobs. The async will only show the exception when you call await().

Exception handling in launch

Now we have a way to handle exceptions in launch. In coroutines we have a class named is CoroutineExceptionHandler

class MainActivity : AppCompatActivity() {
    companion object {
        private const val TAG = "MainActivity"
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        runBlocking {
            val myHandler = CoroutineExceptionHandler {coroutineContext, throwable ->
                Log.e(TAG,"Exception handled: ${throwable.localizedMessage}")
            }

            val job = GlobalScope.launch(myHandler) {
                Log.e(TAG,"Throwing exception from job")
                throw IndexOutOfBoundsException("exception in coroutine")
            }
            job.join()

            val deferred = GlobalScope.async {
                Log.e(TAG,"Throwing exception from async")
                throw ArithmeticException("exception from async")
            }

            try {
                deferred.await()
            } catch (e: java.lang.ArithmeticException) {
                Log.e(TAG,"Caught ArithmeticException ${e.localizedMessage}")
            }
        }
    }
}

Output

 E/MainActivity: Throwing exception from job
 E/MainActivity: Exception handled: exception in coroutine
 E/MainActivity: Throwing exception from async
 E/MainActivity: Caught ArithmeticException exception from async

Conclusion

In this post, we learned how to handle exceptions in coroutines. I have used this exception handling concept in the retrofit example. I hope it’s helpful for you, Help me by sharing this post with all your friends who learning android app development.

Write A Comment