suspendCoroutine异常处理疑惑:resumeWithException适用场景
关于suspendCoroutine中resumeWithException的用法解析
为什么你直接throw也能被捕获?
你测试时替换成throw IOException("TEST")能被try-catch捕获,大概率是巧合——比如Firebase的回调刚好在协程所在的线程执行(比如主线程),此时异常被协程的异常处理机制接住了。但这种情况不可靠,因为回调的执行线程完全由第三方库(这里是Firebase)决定,一旦回调在后台线程执行,直接抛出的异常会被该线程的未捕获异常处理器处理,你的try-catch根本捕获不到,甚至可能导致应用崩溃。
resumeWithException的核心作用
resumeWithException是协程提供的安全传递异常到协程执行流的机制:
- 不管回调在哪个线程触发,它都会把异常发送到协程的上下文,让协程在
getFirebaseToken()的挂起点恢复执行,并在该位置抛出异常。 - 这样你的try-catch块就能稳定捕获到异常,完全符合协程的挂起函数契约。
必须使用resumeWithException的场景
- 回调在非协程线程执行时:比如大多数第三方库的回调(像Firebase、OkHttp的异步回调)都运行在自己的线程池,此时直接throw会导致异常泄露到回调线程,无法被协程的try-catch捕获。
- 确保异常纳入协程异常处理体系:协程有自己的异常处理机制(比如CoroutineExceptionHandler),用
resumeWithException抛出的异常会被这些机制统一处理,而直接throw的异常则不会。 - 遵守挂起函数规范:挂起函数的异常应该通过协程恢复机制抛出,而不是在回调线程中直接抛出,这是编写可维护协程代码的基本准则。
你的代码优化点
另外注意你代码中的一个小问题:addOnCompleteListener里不管任务是否成功,都会执行it.resume(task.result),这会导致任务失败时先调用resumeWithException,然后又调用resume,触发IllegalStateException(因为Continuation只能被恢复一次)。应该修改为:
addOnCompleteListener { task: Task<String> -> if (!task.isSuccessful) { it.resumeWithException(task.exception!!) } else { it.resume(task.result) } }
内容的提问来源于stack exchange,提问作者sarthak gupta
相关产品推荐
相关产品推荐

