How to catch all the exceptions possibly thrown by...
# coroutines
k
How to catch all the exceptions possibly thrown by multiple jobs launched in same scope (SupevisorScope)? Currently I’m doing something like this but no exceptions are getting caught
e
I think a coroutine exception handler only works if installed in the uppermost context. Are you using the handler in a scope within the context of another?
k
You mean
GlobalScope
? I read about that and also found all examples of exception handling shown either in
GlobalScope
or
runBlocking
block. I also think the same but am looking for any other ways to handle my usecase
e
No, I mean where you have a coroutine scope (i.e. one that already has a context and exception handler), but you try to switch context (exception handler is a coroutine context) in a coroutine inside that scope. I think that's not possible: you'd have to create a scope and put your exception handler there
k
The parent scope in my case is
lifecycleScope
of
Fragment
. Idk if it has an exception handler but as you said, I've to create a scope and put exception Handler there, is exactly what I've done. FYI, I also tried to pass exceptionHandler to
launch
&
async
but nothing worked
e
From this article (https://medium.com/androiddevelopers/exceptions-in-coroutines-ce8da1ec060c), see the following code:
Copy code
val handler = CoroutineExceptionHandler {
    context, exception -> println("Caught $exception")
}
val scope = CoroutineScope(Job())
scope.launch(handler) {
    launch {
        throw Exception("Failed coroutine")
    }
}
In the above code the exception will be handled by
handler
. However, the following code will not handle your exception in
handler
, because the handler is not installed in the outer coroutine:
Copy code
val scope = CoroutineScope(Job())
scope.launch {
    launch(handler) {
        throw Exception("Failed coroutine")
    }
}
Quote from the article:
The inner launch will propagate the exception up to the parent as soon as it happens, since the parent doesn’t know anything about the handler, the exception will be thrown.
A few examples: - https://pl.kotl.in/FzqF7pEUw : handler handles exception - https://pl.kotl.in/a-P0Q1ORW : handler handles exception - https://pl.kotl.in/tM-6OlHMV : handler does not handle exception
- https://pl.kotl.in/cWBqHpTOr : handler in the scope instead of the outer coroutine
So in your fragment you should be able to
lifecycleScope.launch(handler) { /* Do exceptional stuff */ }
, or just create your own
CoroutineScope(handler)
, but don't forget to cancel the scope on fragment destroy/stop/pause (depending on when you create the scope and when stuff needs to cancel)
k
But what's wrong with the snippet that I shared ? Why it's not working? As I said, I've tried to launch jobs using
scope.launch(handler) { ... }
But this doesn't work. I'll share a playground link with the code that doesn't work
Also, I believe in playground, we're much closer to
GlobalScope
whereas Fragment have
lifecycleScope
and I'm trying to create custom scope out of it which I believe won't be able to handle exceptions. I'll give a shot to
lifecycleScope.launch(handler) { }
as that seems promising but my purpose was to have a
supervisorScope
, i.e. failing any child coroutine should not cause others to fail and neither cause any Runtime Exception. I'm launching independent network calls which might fail and I've a code associated with it to reflect those errors on UI, the problem is, handling exceptions seperately for each job gives me the desired effect but I lose the ability to get to know whether all the jobs are successful so that I can show Success Toast/Snackbar at the very end
e
You might be able to use
async {}
for that: do many calls in parallel and then
await()
all `Deferred<T>`: awaiting rethrows exceptions that happened in the block which you can simply
try
-
catch
. If anything you expect is caught, you can show your UI notice.
k
Yes. It works that way. I was just concerned why it doesn't work with
exceptionHandler