Search code examples
kotlinasynchronoustry-catchkotlin-coroutinescoroutine

Why i can't use try/catch to catch exception in Kotlin coroutine?


I have code in Android as follow:


//use MainScope
private val scope = MainScope()

private fun test() {
    scope.launch {
        Log.d(TAG, "test: launch")
        try {
            val response: Deferred<String> = async {
                Log.d(TAG, "test: in async block")
                throw IllegalStateException("an IllegalStateException")
            }
            response.await()
        } catch (e: Exception) {
            Log.d(TAG, "test: error ${e.message}")
        }
    }
}

I use MainScope to launch a coroutine and use async to launch a child coroutine in it. When i use try/catch to catch the exception of child coroutine , i failed and the application crashed. Could some one tell me why ?

Crash messages as follow:

D/AsyncExceptionTestActiv: test: launch
D/AsyncExceptionTestActiv: test: in async block
D/AsyncExceptionTestActiv: test: error an IllegalStateException
E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.hm.dumingwei.kotlinandroid, PID: 18461
    java.lang.IllegalStateException: an IllegalStateException
        at com.hm.dumingwei.kotlinandroid.AsyncExceptionTestActivity$test$1$response$1.invokeSuspend(AsyncExceptionTestActivity.kt:61)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:241)
        at android.os.Handler.handleCallback(Handler.java:761)
        at android.os.Handler.dispatchMessage(Handler.java:98)
        at android.os.Looper.loop(Looper.java:156)
        at android.app.ActivityThread.main(ActivityThread.java:6517)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:942)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:832)

Solution

  • The exception thrown inside nested coroutine is propagated to the parent coroutine unless you use a SupervisorJob:

    scope.launch {
        Log.d(TAG, "test: launch")
        try {
            val response: Deferred<String> = async(SupervisorJob()) {
                Log.d(TAG, "test: in async block")
                throw IllegalStateException("an IllegalStateException")
            }
            response.await()
        } catch (e: Exception) {
            Log.d(TAG, "test: error ${e.message}")
        }
    }
    

    Kotlin Documentation, info, more info