注:编码工具为IntelliJ
目录
Kotlin异常
不处理的情况
处理的情况
协程异常处理
launch方式启动的协程异常抛出和处理
异常抛出
常规处理:try-catch
常规处理二:try-catch包裹整个协程,不起作用
CoroutineExceptionHandler? ?
async方式启动的协程异常抛出和处理
异常抛出
处理异常
CoroutineExceptionHandler
全局协程异常处理器
协同作用域与主从作用域异常处理的不同:针对协程嵌套
协同作用域
主从作用域
Kotlin异常
????????Kotlin中没有受检查异常,如果某段代码可能发生异常,需要手动try-catch。
不处理的情况
package step_fourteen
import java.io.File
fun main() {
File("").readLines()
}
输出:
Exception in thread "main" java.io.FileNotFoundException:
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at kotlin.io.FilesKt__FileReadWriteKt.forEachLine(FileReadWrite.kt:190)
at kotlin.io.FilesKt__FileReadWriteKt.readLines(FileReadWrite.kt:219)
at kotlin.io.FilesKt__FileReadWriteKt.readLines$default(FileReadWrite.kt:217)
at step_fourteen.KotlinExceptionKt.main(KotlinException.kt:6)
at step_fourteen.KotlinExceptionKt.main(KotlinException.kt)
处理的情况
package step_fourteen
import step_twelve.log
import java.io.File
import java.lang.Exception
fun main() {
try{
File("").readLines()
}catch (e: Exception)
{
log("捕获一个异常")
}
}
输出:
[2021-11-27 11:29:53]-[main] 捕获一个异常
协程异常处理
launch方式启动的协程异常抛出和处理
异常抛出
package step_fourteen
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import step_twelve.log
import java.io.File
fun main() {
val scope = CoroutineScope(Job())
scope.launch {
log("before")
File("").readLines()
log("after")
}
Thread.sleep(200)
}
输出:
[2021-11-27 11:32:08]-[DefaultDispatcher-worker-1] before
Exception in thread "DefaultDispatcher-worker-1" java.io.FileNotFoundException:
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at kotlin.io.FilesKt__FileReadWriteKt.forEachLine(FileReadWrite.kt:190)
at kotlin.io.FilesKt__FileReadWriteKt.readLines(FileReadWrite.kt:219)
at kotlin.io.FilesKt__FileReadWriteKt.readLines$default(FileReadWrite.kt:217)
at step_fourteen.LaunchExceptionKt$main$1.invokeSuspend(LaunchException.kt:13)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
常规处理:try-catch
package step_fourteen
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import step_twelve.log
import java.io.File
fun main() {
GlobalScope.launch {
log("before")
try{
File("").readLines()
}catch (e: Exception)
{
log("捕获异常")
}
log("after")
}
Thread.sleep(200)
}
输出:
[2021-11-27 11:34:33]-[DefaultDispatcher-worker-1] before
[2021-11-27 11:34:33]-[DefaultDispatcher-worker-1] 捕获异常
[2021-11-27 11:34:33]-[DefaultDispatcher-worker-1] after
常规处理二:try-catch包裹整个协程,不起作用
package step_fourteen
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import step_twelve.log
import java.io.File
fun main() {
try {
GlobalScope.launch {
log("before")
File("").readLines()
log("after")
}
} catch (e: Exception) {
log("捕获异常")
}
Thread.sleep(200)
}
输出:
[2021-11-27 11:35:02]-[DefaultDispatcher-worker-1] before
Exception in thread "DefaultDispatcher-worker-1" java.io.FileNotFoundException:
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at kotlin.io.FilesKt__FileReadWriteKt.forEachLine(FileReadWrite.kt:190)
at kotlin.io.FilesKt__FileReadWriteKt.readLines(FileReadWrite.kt:219)
at kotlin.io.FilesKt__FileReadWriteKt.readLines$default(FileReadWrite.kt:217)
at step_fourteen.LaunchExceptionKt$main$1.invokeSuspend(LaunchException.kt:12)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
CoroutineExceptionHandler? ?
????????虽然捕获了异常,但是协程还是被取消了。
package step_fourteen
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import step_twelve.log
import java.io.File
fun main() {
val handler = CoroutineExceptionHandler { _, e ->
log("handler 捕获异常 ")
}
GlobalScope.launch(handler) {
log("before")
File("").readLines()
log("after")
}
Thread.sleep(200)
}
输出:
[2021-11-27 11:36:59]-[DefaultDispatcher-worker-2] before
[2021-11-27 11:36:59]-[DefaultDispatcher-worker-2] handler 捕获异常
? ? ? ? handler对嵌套协程也适用,发生异常的协程会被取消,其他协程不受影响。
package step_fourteen
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import step_twelve.log
import java.io.File
fun main() {
val handler = CoroutineExceptionHandler { _, e ->
log("handler 捕获异常 ")
}
GlobalScope.launch(handler) {
launch {
log("before")
File("").readLines()
log("after")
}
launch {
log("子协程")
}
}
Thread.sleep(200)
}
输出:
[2021-11-27 11:40:09]-[DefaultDispatcher-worker-3] 子协程
[2021-11-27 11:40:09]-[DefaultDispatcher-worker-1] before
[2021-11-27 11:40:09]-[DefaultDispatcher-worker-1] handler 捕获异常
? ? ? ? handler作为子协程的CoroutineContext,无效。
package step_fourteen
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import step_twelve.log
import java.io.File
fun main() {
val handler = CoroutineExceptionHandler { _, e ->
log("handler 捕获异常 ")
}
GlobalScope.launch() {
launch(handler) {
log("before")
File("").readLines()
log("after")
}
launch {
log("子协程")
}
}
Thread.sleep(200)
}
输出:
[2021-11-27 11:43:22]-[DefaultDispatcher-worker-3] 子协程
[2021-11-27 11:43:22]-[DefaultDispatcher-worker-2] before
Exception in thread "DefaultDispatcher-worker-2" java.io.FileNotFoundException:
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at kotlin.io.FilesKt__FileReadWriteKt.forEachLine(FileReadWrite.kt:190)
at kotlin.io.FilesKt__FileReadWriteKt.readLines(FileReadWrite.kt:219)
at kotlin.io.FilesKt__FileReadWriteKt.readLines$default(FileReadWrite.kt:217)
at step_fourteen.LaunchExceptionKt$main$1$1.invokeSuspend(LaunchException.kt:16)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
? ? ? ? 小结:handler只在作为顶级作用域的协程上下文时有效。
async方式启动的协程异常抛出和处理
异常抛出
? ? ? ? 不抛出情况
package step_fourteen
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import step_twelve.log
import java.io.File
fun main() {
val scope = CoroutineScope(Job())
scope.async{
log("before")
File("").readLines()
log("after")
}
Thread.sleep(200)
}
输出:
[2021-11-27 11:49:50]-[DefaultDispatcher-worker-1] before
????????抛出情况
package step_fourteen
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
import step_twelve.log
import java.io.File
fun main() {
runBlocking {
val scope = CoroutineScope(Job())
scope.async{
log("before")
File("").readLines()
log("after")
}.await()
Thread.sleep(200)
}
}
输出:
[2021-11-27 11:50:35]-[DefaultDispatcher-worker-1] before
Exception in thread "main" java.io.FileNotFoundException:
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at kotlin.io.FilesKt__FileReadWriteKt.forEachLine(FileReadWrite.kt:190)
at kotlin.io.FilesKt__FileReadWriteKt.readLines(FileReadWrite.kt:219)
at kotlin.io.FilesKt__FileReadWriteKt.readLines$default(FileReadWrite.kt:217)
at step_fourteen.AsyncExceptionKt$main$1$1.invokeSuspend(AsyncException.kt:15)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
处理异常
? ? ? ? try-catch
package step_fourteen
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
import step_twelve.log
import java.io.File
fun main() {
runBlocking {
val scope = CoroutineScope(Job())
val def = scope.async {
log("before")
File("").readLines()
log("after")
}
try {
def.await()
} catch (e: Exception) {
log("捕获异常")
}
Thread.sleep(200)
}
}
输出:
[2021-11-27 11:52:13]-[DefaultDispatcher-worker-1] before
[2021-11-27 11:52:13]-[main] 捕获异常
CoroutineExceptionHandler
package step_fourteen
import kotlinx.coroutines.*
import step_twelve.log
import java.io.File
suspend fun main() {
val handler = CoroutineExceptionHandler { _, e ->
log("handler 捕获异常")
}
val scope = CoroutineScope(Job())
val def = scope.async(handler) {
log("before")
File("").readLines()
log("after")
}
def.await()
Thread.sleep(200)
}
输出:
[2021-11-27 11:54:03]-[DefaultDispatcher-worker-1] before
Exception in thread "main" java.io.FileNotFoundException:
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at kotlin.io.FilesKt__FileReadWriteKt.forEachLine(FileReadWrite.kt:190)
at kotlin.io.FilesKt__FileReadWriteKt.readLines(FileReadWrite.kt:219)
at kotlin.io.FilesKt__FileReadWriteKt.readLines$default(FileReadWrite.kt:217)
at step_fourteen.AsyncExceptionKt$main$def$1.invokeSuspend(AsyncException.kt:15)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
? ? ? ? 小结:CoroutineExceptionHandler对launch启动方式有效,对async启动方式无效。
全局协程异常处理器
? ? ? ? 首先定义一个类实现CoroutineExceptionHandler
package step_fourteen
import kotlinx.coroutines.CoroutineExceptionHandler
import step_twelve.log
import kotlin.coroutines.CoroutineContext
class GlobalExceptionHandler: CoroutineExceptionHandler {
override val key: CoroutineContext.Key<*>
get() = CoroutineExceptionHandler
override fun handleException(context: CoroutineContext, exception: Throwable) {
Thread.setDefaultUncaughtExceptionHandler { t, e -> log("全局Handler捕获异常")}
}
}
? ? ? ? src/main目录下创建resources目录,resources目录下创建META-INF目录,META-INF目录下创建services目录,services目录下创建名为kotlinx.coroutines.CoroutineExceptionHandler的文件,将自定义的Handler的全路径名写入其中,然后全局协程异常处理器就生效了。
?
?全局异常处理器测试
package step_fourteen
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import step_twelve.log
import java.io.File
fun main() {
GlobalScope.launch {
log("before")
File("").readLines()
log("after")
}
Thread.sleep(200)
}
输出:
[2021-11-27 12:05:55]-[DefaultDispatcher-worker-2] before
[2021-11-27 12:05:55]-[DefaultDispatcher-worker-2] 全局Handler捕获异常
????????全局异常处理器生效的原理:用到了JVM的ServiceLoader。
? ? ? ? 在源码的CoroutineExceptionHandlerImpl.kt文件中用ServiceLoader加载了我们自己定义的类,然后进行相应处理。
private val handlers: List<CoroutineExceptionHandler> = ServiceLoader.load(
CoroutineExceptionHandler::class.java,
CoroutineExceptionHandler::class.java.classLoader
).iterator().asSequence().toList()
internal actual fun handleCoroutineExceptionImpl(context: CoroutineContext, exception: Throwable) {
// use additional extension handlers
for (handler in handlers) {
try {
handler.handleException(context, exception)
} catch (t: Throwable) {
// Use thread's handler if custom handler failed to handle exception
val currentThread = Thread.currentThread()
currentThread.uncaughtExceptionHandler.uncaughtException(currentThread, handlerException(exception, t))
}
}
// use thread's handler
val currentThread = Thread.currentThread()
currentThread.uncaughtExceptionHandler.uncaughtException(currentThread, exception)
}
协同作用域与主从作用域异常处理的不同:针对协程嵌套
协同作用域
? ? ? ? 只要有一个子协程发生异常,所有协程都会被取消。
package step_fourteen
import kotlinx.coroutines.*
import step_twelve.log
fun main() {
GlobalScope.launch() {
launch() {
throw RuntimeException("abc")
}
launch {
log("test")
}
}
Thread.sleep(100)
}
输出:
Exception in thread "DefaultDispatcher-worker-1" java.lang.RuntimeException: abc
at step_fourteen.ScopeTestKt$main$1$1.invokeSuspend(ScopeTest.kt:9)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
? ? ? ? 可以看到上面的子协程抛出异常,下面的子协程没有打印test。
主从作用域
? ? ? ? 有某个子协程发生异常,不影响其他协程运行。
package step_fourteen
import kotlinx.coroutines.*
import step_twelve.log
fun main() {
GlobalScope.launch() {
supervisorScope {
launch() {
throw RuntimeException("abc")
}
launch {
log("test")
}
}
}
Thread.sleep(100)
}
输出:
Exception in thread "DefaultDispatcher-worker-2" java.lang.RuntimeException: abc
at step_fourteen.ScopeTest2Kt$main$1$1$1.invokeSuspend(ScopeTest2.kt:10)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
[2021-11-27 12:14:14]-[DefaultDispatcher-worker-3] test
? ? ? ? 可以看到,虽然上面的协程抛出了异常,但下面的协程依然正常运行。
总结:
? ? ? ? 1、Job(launch启动方式返回值)的异常需要在根协程域捕获,类似 Thread.defaultUncaughtExceptionHandler,在子协程无法捕获,如果不捕获,会崩溃。
? ? ? ? 2、Defered(async启动方式返回值)的异常依赖用户来最终消费(await)异常,如果没消费,不需要捕获,也不会崩溃,如果消费了则需要捕获,不然会崩溃。
? ? ? ? 3、协同作用域中,协程中异常传播是双向的,子协程的异常会导致父协程结束,父协程的异常也会导致整个结束。
? ? ? ? 4、主从作用域中,SupervisorJob的异常是单向传播的,子协程的异常不影响其他,父协程的异常会影响全局,因此,SupervisorJob的子协程都应该设置捕获异常操作。 ?????????注意: 只有一级子协程和父协程之间才是主从关系: 单向传播; 而二级及以上子协程与父协程之间没有特殊声明,就是协同关系:双向传播。
|