kotlin async exception handling
Solution 1
You can find your answer here: https://proandroiddev.com/kotlin-coroutines-patterns-anti-patterns-f9d12984c68e
To summarise, there are a few ways to catch exceptions with async
.
1 - Wrap async
call with supervisorScope
launch {
supervisorScope {
val task = async {
methodThatThrowsException()
}
try {
updateUI("Ok ${task.await()}")
} catch (e: Throwable) {
showError("Erro! ${e.message}")
}
}
}
2 - Passing a SupervisorJob
as param
launch {
// parentJob (optional) is the parent Job of the CoroutineContext
val task = async(SupervisorJob(parentJob)) {
methodThatThrowsException()
}
try {
updateUI("Ok ${task.await()}")
} catch (e: Throwable) {
showError("Erro! ${e.message}")
}
}
3 - Wrapping async
with coroutineScope
launch {
try {
coroutineScope {
val task = async {
methodThatThrowsException()
}
updateUI("Ok ${task.await()}")
}
} catch (e: Throwable) {
showError("Erro! ${e.message}")
}
}
Solution 2
In your second example, if you add Thread.sleep(1000)
after your println("main end")
statement you will see an exception as well. Without the sleep
the application ends before the exception is thrown:
Exception in thread "DefaultDispatcher-worker-3" java.lang.Exception: aaa
at de.e2.app$Companion$main$job$1$1.invokeSuspend(AsyncProblem2.kt:26)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)
at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:236)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:742)
In both cases you run into a standard behavior of structured concurrency introduced with Kotlin 1.3 (see https://medium.com/@elizarov/structured-concurrency-722d765aa952).
If in an async
block an exception is thrown, the own coroutine is cancelled and the parent coroutines as well: see Kotlin coroutine can't handle exception
IulianT
Updated on June 14, 2022Comments
-
IulianT almost 2 years
Given the following snippet, i do not understand why my android app crashes. I tested in a standalone kotlin app but this does not happen.
class LoginActivity : AppCompatActivity(), CoroutineScope { lateinit var job: Job override val coroutineContext: CoroutineContext get() = Dispatchers.Main + job override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) job = Job() try { launch() { try { var res = async { test() } res.await() } catch (e2: java.lang.Exception) { } } } catch (e: java.lang.Exception) { } } fun test(): String { throw java.lang.Exception("test ex") return ""; } } --------- beginning of crash E/AndroidRuntime: FATAL EXCEPTION: main Process: ro.ingr.ingeeasafety, PID: 11298 java.lang.Exception: test ex at ro.ingr.ingeeasafety.activities.LoginActivity.test(LoginActivity.kt:72) at ro.ingr.ingeeasafety.activities.LoginActivity$onCreate$1$res$1.invokeSuspend(LoginActivity.kt:48) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32) at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:236) at android.os.Handler.handleCallback(Handler.java:751) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:154) at android.app.ActivityThread.main(ActivityThread.java:6119) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
Standalone kotlin app code, execution reaches "main end" println
class app { companion object :CoroutineScope { lateinit var job: Job override val coroutineContext: CoroutineContext get() = Dispatchers.Default+ job init { job=Job() } @JvmStatic fun main(args: Array<String>) { launch() { try { async() { println("async start") throw Exception("aaa") }.await() } catch (e: Exception) { println("async exception") } } println("main end") } } }
I am trying to create a flow where i load something from somewhere and if the load operation fails my app does not crash. I was expecting that the exception got caught in the handlers defined.
LE: I added the crash stack trace.