Hi guys, I need help with `Job`. When I add a `Job...
# coroutines
s
Hi guys, I need help with
Job
. When I add a
Job
instance to a
CoroutineContext
the behaviour of
async
changes in regards to exception handling. Suddenly exceptions within
async
are propagated to the default exception handler. On Android for example this causes the app to crash.
try .. catch
around
await()
don't help here. It seems that
Job
adds the default exception handler to the context. However I need
Job
so that I can cancel the context later. Here's an example (yes, it's Android but it's a generic problem, not related to Android)
Copy code
class TestActivity : Activity(),
                     CoroutineScope {

    override val coroutineContext: CoroutineContext = Dispatchers.Default + Job()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val deferred = async {
            // App crashes here
            throw NullPointerException()
        }

        launch {
            try {
                deferred.await()
            } catch (e: Exception) {
                Log.e("TestActivity", "ERROR", e)
            }
        }
    }

    override fun onDestroy() {
        coroutineContext.cancel()
        super.onDestroy()
    }
}
How can I have the default behaviour of
async
where exceptions are only thrown on
await()
but also have the ability of a cancellable job in a context?
Would this be the right job (haha) for a
SupervisorJob
?
l
You just broke structured concurrency. Just look and understand the slides from the one I linked to see the problem and see why you should use an enclosing `coroutineScope { … }`: https://speakerdeck.com/elizarov/kotlin-1-dot-3-webinar-coroutines-presentation?slide=79
s
I don't understand why I broke structured concurrency. Having an
Activity
implement
CoroutineScope
is a common pattern in Android development with coroutines. All coroutines launched in this scope are cancelled when the Activity is destroyed (as demonstrated above). I understand the use case in the slide but in this case I only have one
async
anyway. I understand that if I have a dependency between multiple
async
, I should wrap them in
coroutineScope
. However the outer Activity scope should stay imho.
g
It seems that
Job
adds the default exception handler to the context
Yes, it works as designed, your scope is cancelled and throw exception
use SupervisorJob istead
s
I saw that doc but what was not clear to me is that adding a
Job
to a
CoroutineContext
changes the exception handling
But know I understand why. The original code was written before
SupervisorJob
was introduced and the problem only evident now
l
You're right, I looked too quickly, you're in an
Activity
, so Andrey's advice is the right one.
👍 1
g
SupervisorJob was introduced together with Structured Concurrency, but some examples actually used Job for UI components, later many of them were fixed