• Kotlin协程-try-catch基础


                在 Kotlin 协程当中,我们通常把异常分为两大类,一类是取消异常(CancellationException),另一类是其他异常。在 Kotlin 协程当中,这两种异常的处理方式是不一样的。

            一、cancel()无效?

            当协程任务被取消的时候,它的内部是会产生一个 CancellationException 的。而协程的结构化并发,最大的优势就在于:如果我们取消了父协程,子协程也会跟着被取消。

            1.cancel()不被响应

    1. val job = launch(Dispatchers.Default) {
    2. var i = 0
    3. while (true) {
    4. Thread.sleep(500L)
    5. i++
    6. println("i =$i")
    7. }
    8. }
    9. delay(2000L)
    10. job.cancel()
    11. job.join()
    12. println("END")
    13. }
    14. Log
    15. i =1
    16. i =2
    17. i =3
    18. i =4
    19. i =5
    20. i =6
    21. i =7
    22. i =8
    23. i =9
    24. i =10
    25. i =11
    26. i =12
    27. ......
    28. 程序无法停止

    上面的程序无法停止,协程任务的取消,需要互相协作。协程外部取消,协程内部需要做出响应才行。当我们调用 job.cancel() 以后,协程任务已经不是活跃状态了,但代码并没有把 isActive 作为循环条件,因此协程无法真正取消。

    可以在协程体中加入状态判断:

    1. runBlocking {
    2. val job = launch(Dispatchers.Default) {
    3. var i = 0
    4. while (isActive) {
    5. Thread.sleep(500L)
    6. i++
    7. println("i =$i")
    8. }
    9. }
    10. delay(2000L)
    11. job.cancel()
    12. job.join()
    13. println("END")
    14. }
    15. Log
    16. i =1
    17. i =2
    18. i =3
    19. i =4
    20. END
    21. Process finished with exit code 0

    把 while 循环的条件改成了 while (isActive),这就意味着,只有协程处于活跃状态的时候,才会继续执行循环体内部的代码。协程的取消需要内部的配合。

    2.结构被破坏

    协程是结构化的,当我们取消父协程的时候,子协程也会跟着被取消。

    但是特殊情况是嵌套创建的子协程并不会跟随父协程一起取消。

    1. runBlocking {
    2. val parentJob = launch(fixedDispatcher) {
    3. launch(Job()) {
    4. var i = 0
    5. while (isActive) {
    6. Thread.sleep(500L)
    7. i++
    8. println("First i:$i")
    9. }
    10. }
    11. launch {
    12. var i = 0
    13. while (isActive) {
    14. Thread.sleep(500L)
    15. i++
    16. println("Second i:$i")
    17. }
    18. }
    19. }
    20. delay(2000L)
    21. parentJob.cancel()
    22. parentJob.join()
    23. println("End")
    24. }
    25. Log
    26. Second i:1
    27. First i:1
    28. First i:2
    29. Second i:2
    30. Second i:3
    31. First i:3
    32. Second i:4
    33. First i:4
    34. End
    35. First i:5
    36. First i:6
    37. First i:7
    38. First i:8
    39. First i:9
    40. First i:10
    41. First i:11
    42. First i:12
    43. First i:13
    44. First i:14
    45. First i:15
    46. ......

    可以发现,创建子协程的时候,使用了 launch(Job()){},就打破了原有的协程结构。因为 launch(Job()){}创建的协程的父 Job 是在 launch 当中传入的 Job() 对象。所以调用 parentJob.cancel() 的时候,无法销毁该协程。

    可以按如下修改:

    1. runBlocking {
    2. val parentJob = launch(fixedDispatcher) {
    3. launch {
    4. var i = 0
    5. while (isActive) {
    6. Thread.sleep(500L)
    7. i++
    8. println("First i:$i")
    9. }
    10. }
    11. launch {
    12. var i = 0
    13. while (isActive) {
    14. Thread.sleep(500L)
    15. i++
    16. println("Second i:$i")
    17. }
    18. }
    19. }
    20. delay(2000L)
    21. parentJob.cancel()
    22. parentJob.join()
    23. println("End")
    24. }
    25. First i:1
    26. Second i:1
    27. First i:2
    28. Second i:2
    29. First i:3
    30. Second i:3
    31. First i:4
    32. Second i:4
    33. End

    parentJob 与它内部的子协程之间都是父子关系,因此它们两个都是会响应协程取消的事件的。不要轻易打破协程的父子结构!

    3.未正确处理 CancellationException

    对于 Kotlin 提供的挂起函数,可以自动响应协程的取消。

    例如:

    1. runBlocking {
    2. val parentJob = launch(Dispatchers.Default) {
    3. launch {
    4. var i = 0
    5. while (true) {
    6. delay(500L)
    7. i++
    8. println("First i = $i")
    9. }
    10. }
    11. launch {
    12. var i = 0
    13. while (true) {
    14. delay(500L)
    15. i++
    16. println("Second i = $i")
    17. }
    18. }
    19. }
    20. delay(2000L)
    21. parentJob.cancel()
    22. parentJob.join()
    23. println("End")
    24. }
    25. First i = 1
    26. Second i = 1
    27. First i = 2
    28. Second i = 2
    29. First i = 3
    30. Second i = 3
    31. End
    32. Process finished with exit code 0

     delay() 函数可以自动检测当前的协程是否已经被取消,如果已经被取消的话,它会抛出一个 CancellationException,从而终止当前的协程。

    1. runBlocking {
    2. val parentJob = launch(Dispatchers.Default) {
    3. launch {
    4. var i = 0
    5. while (true) {
    6. try {
    7. delay(500L)
    8. } catch (e: CancellationException) {
    9. println("Catch CancellationException")
    10. throw e
    11. }
    12. i++
    13. println("First i =$i")
    14. }
    15. }
    16. launch {
    17. var i = 0
    18. while (true) {
    19. delay(500L)
    20. i++
    21. println("Second i = $i")
    22. }
    23. }
    24. }
    25. delay(2000L)
    26. parentJob.cancel()
    27. parentJob.join()
    28. println("END")
    29. }
    30. Log:
    31. First i =1
    32. Second i = 1
    33. First i =2
    34. Second i = 2
    35. First i =3
    36. Second i = 3
    37. Catch CancellationException
    38. END
    39. Process finished with exit code 0

    try-catch 包裹了 delay() 以后,打印出“Catch CancellationException”,这就说明 delay() 确实可以自动响应协程的取消,并且产生 CancellationException 异常。

    注意:捕获了 CancellationException 以后没有重新抛出去,就导致子协程无法正常取消。

    1. runBlocking {
    2. val parentJob = launch(Dispatchers.Default) {
    3. launch {
    4. var i = 0
    5. while (true) {
    6. try {
    7. delay(500L)
    8. } catch (e: CancellationException) {
    9. println("Catch CancellationException")
    10. //throw e
    11. }
    12. i++
    13. println("First i =$i")
    14. }
    15. }
    16. launch {
    17. var i = 0
    18. while (true) {
    19. delay(500L)
    20. i++
    21. println("Second i = $i")
    22. }
    23. }
    24. }
    25. delay(2000L)
    26. parentJob.cancel()
    27. parentJob.join()
    28. println("END")
    29. }
    30. ......
    31. First i =656179
    32. Catch CancellationException
    33. First i =656180
    34. Catch CancellationException
    35. First i =656181
    36. Catch CancellationException
    37. First i =656182
    38. Catch CancellationException
    39. First i =656183
    40. Catch CancellationException
    41. .....

    所以,捕获了 CancellationException 以后,要考虑是否应该重新抛出来。

    二、 try-catch 不起作用?

    1. runBlocking {
    2. try {
    3. launch {
    4. delay(100L)
    5. 1 / 0
    6. }
    7. } catch (e: Exception) {
    8. println("catch: $e")
    9. }
    10. delay(500L)
    11. println("End")
    12. }
    13. Log
    14. Exception in thread "main" java.lang.ArithmeticException: / by zero
    15. at com.example.myapplication.testcoroutinue.TestTryCatchKt$testTryCatch8$1$1.invokeSuspend(TestTryCatch.kt:225)
    16. at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    17. at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTask.kt:234)
    18. at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:166)
    19. at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:397)
    20. at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:431)
    21. at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default(CancellableContinuationImpl.kt:420)
    22. at kotlinx.coroutines.CancellableContinuationImpl.resumeUndispatched(CancellableContinuationImpl.kt:518)
    23. at kotlinx.coroutines.EventLoopImplBase$DelayedResumeTask.run(EventLoop.common.kt:494)
    24. at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:279)
    25. at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
    26. at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
    27. at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
    28. at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
    29. at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
    30. at com.example.myapplication.testcoroutinue.TestTryCatchKt.testTryCatch8(TestTryCatch.kt:221)
    31. at com.example.myapplication.testcoroutinue.TestTryCatchKt.main(TestTryCatch.kt:15)
    32. at com.example.myapplication.testcoroutinue.TestTryCatchKt.main(TestTryCatch.kt)
    33. Process finished with exit code 1

    可以发现:try-catch 并没有成功捕获异常,程序等待了 100 毫秒左右,最终还是崩溃了。

    使用async

    1. runBlocking {
    2. var deffered: Deferred? = null
    3. try {
    4. deffered = async {
    5. delay(100L)
    6. 1 / 0
    7. }
    8. } catch (e: ArithmeticException) {
    9. println("Catch:$e")
    10. }
    11. deffered?.await()
    12. println("End")
    13. }
    14. Exception in thread "main" java.lang.ArithmeticException: / by zero
    15. at com.example.myapplication.testcoroutinue.TestTryCatchKt$testTryCatch9$1$1.invokeSuspend(TestTryCatch.kt:242)
    16. at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    17. at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTask.kt:234)
    18. at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:166)
    19. at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:397)
    20. at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:431)
    21. at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default(CancellableContinuationImpl.kt:420)
    22. at kotlinx.coroutines.CancellableContinuationImpl.resumeUndispatched(CancellableContinuationImpl.kt:518)
    23. at kotlinx.coroutines.EventLoopImplBase$DelayedResumeTask.run(EventLoop.common.kt:494)
    24. at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:279)
    25. at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
    26. at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
    27. at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
    28. at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
    29. at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
    30. at com.example.myapplication.testcoroutinue.TestTryCatchKt.testTryCatch9(TestTryCatch.kt:237)
    31. at com.example.myapplication.testcoroutinue.TestTryCatchKt.main(TestTryCatch.kt:16)
    32. at com.example.myapplication.testcoroutinue.TestTryCatchKt.main(TestTryCatch.kt)
    33. Process finished with exit code 1

    当协程体当中的“1/0”执行的时候,程序已经跳出 try-catch 的作用域了,所以 try-catch失效。

    把 try-catch 挪到 launch{} 协程体内部。可以正常捕获到 ArithmeticException 这个异常了。

    1. runBlocking {
    2. var deffered: Deferred? = null
    3. deffered = async {
    4. try {
    5. delay(100L)
    6. 1 / 0
    7. } catch (e: ArithmeticException) {
    8. println("Catch:$e")
    9. }
    10. }
    11. deffered.await()
    12. println("End")
    13. }
    14. Log
    15. Catch:java.lang.ArithmeticException: / by zero
    16. End
    17. Process finished with exit code 0

    注意:不要用 try-catch 直接包裹 launch、async。

     

    使用 try-catch 包裹“deferred.await()”。

    例:

    1. runBlocking {
    2. var deffered = async {
    3. delay(100L)
    4. 1 / 0
    5. }
    6. try {
    7. deffered.await()
    8. } catch (e: Exception) {
    9. println("Catch:$e")
    10. }
    11. println("End")
    12. }
    13. atch:java.lang.ArithmeticException: / by zero
    14. End
    15. Exception in thread "main" java.lang.ArithmeticException: / by zero
    16. at com.example.myapplication.testcoroutinue.TestTryCatchKt$testTryCatch11$1$deffered$1.invokeSuspend(TestTryCatch.kt:275)
    17. at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    18. at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTask.kt:234)
    19. at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:166)
    20. at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:397)
    21. at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:431)
    22. at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default(CancellableContinuationImpl.kt:420)
    23. at kotlinx.coroutines.CancellableContinuationImpl.resumeUndispatched(CancellableContinuationImpl.kt:518)
    24. at kotlinx.coroutines.EventLoopImplBase$DelayedResumeTask.run(EventLoop.common.kt:494)
    25. at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:279)
    26. at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
    27. at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
    28. at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
    29. at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
    30. at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
    31. at com.example.myapplication.testcoroutinue.TestTryCatchKt.testTryCatch11(TestTryCatch.kt:272)
    32. at com.example.myapplication.testcoroutinue.TestTryCatchKt.main(TestTryCatch.kt:16)
    33. at com.example.myapplication.testcoroutinue.TestTryCatchKt.main(TestTryCatch.kt)
    34. Process finished with exit code 1

    await() 如果不调用的话,async 当中的异常是否发生?

    1. runBlocking {
    2. var deffered = async {
    3. delay(100L)
    4. 1 / 0
    5. }
    6. delay(500L)
    7. println("End")
    8. }
    9. Exception in thread "main" java.lang.ArithmeticException: / by zero
    10. at com.example.myapplication.testcoroutinue.TestTryCatchKt$testTryCatch12$1$deffered$1.invokeSuspend(TestTryCatch.kt:290)
    11. at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    12. at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTask.kt:234)
    13. at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:166)
    14. at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:397)
    15. at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:431)
    16. at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default(CancellableContinuationImpl.kt:420)
    17. at kotlinx.coroutines.CancellableContinuationImpl.resumeUndispatched(CancellableContinuationImpl.kt:518)
    18. at kotlinx.coroutines.EventLoopImplBase$DelayedResumeTask.run(EventLoop.common.kt:494)
    19. at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:279)
    20. at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
    21. at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
    22. at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
    23. at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
    24. at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
    25. at com.example.myapplication.testcoroutinue.TestTryCatchKt.testTryCatch12(TestTryCatch.kt:287)
    26. at com.example.myapplication.testcoroutinue.TestTryCatchKt.main(TestTryCatch.kt:16)
    27. at com.example.myapplication.testcoroutinue.TestTryCatchKt.main(TestTryCatch.kt)
    28. Process finished with exit code 1

    可见,async 当中产生异常,即使不调用 await() 同样是会导致程序崩溃的。

    三、SupervisorJob

    使用 try-catch 包裹“deferred.await()”,需要配合 SupervisorJob 一起使用。实现“不调用 await() 就不会产生异常而崩溃”。

    1. unBlocking {
    2. val scope = CoroutineScope(SupervisorJob())
    3. scope.async {
    4. delay(100L)
    5. 1 / 0
    6. }
    7. delay(500L)
    8. println("End")
    9. }
    10. Log
    11. End
    12. Process finished with exit code 0

     使用 SupervisorJob 创建一个 scope 以后,用 scope.async{}启动协程后,只要不调用“deferred.await()”,程序就不会因为异常而崩溃。

    1. runBlocking {
    2. val coroutineScope = CoroutineScope(SupervisorJob())
    3. val deferred = coroutineScope.async {
    4. delay(100L)
    5. 1 / 0
    6. }
    7. try {
    8. deferred.await()
    9. } catch (e: Exception) {
    10. println("Catch:$e")
    11. }
    12. delay(500L)
    13. println("End")
    14. }
    15. Log
    16. Catch:java.lang.ArithmeticException: / by zero
    17. End
    18. Process finished with exit code 0

    使用“coroutineScope.async {}”创建了协程,同时也用 try-catch 包裹“deferred.await()”,这样一来,异常就成功地被捕获了。

    1. public fun SupervisorJob(parent: Job? = null) : CompletableJob
    2. = SupervisorJobImpl(parent)
    3. public interface CompletableJob : Job {
    4. public fun complete(): Boolean
    5. public fun completeExceptionally(exception: Throwable): Boolean
    6. }

    SupervisorJob() 不是构造函数,它只是一个普通的顶层函数。这个方法返回的对象,是 Job 的子类。SupervisorJob 与 Job 最大的区别就在于,当它的子 Job 发生异常的时候,其他的子 Job 不会受到牵连。

    对于普通 Job, 出现异常时的应对策略是:由于 parentJob 是一个普通的 Job 对象,当 job1 发生异常之后,它会导致 parentJob 取消,进而导致 job2、job3 也受到牵连。

    如果把 parentJob 改为 SupervisorJob,job1 发生异常的的话,就不会影响到其他的 Job 了。

    注意:灵活使用 SupervisorJob,控制异常传播的范围。

    四、CoroutineExceptionHandler

    1. runBlocking {
    2. val coroutineScope = CoroutineScope(coroutineContext)
    3. coroutineScope.launch {
    4. async {
    5. delay(100L)
    6. }
    7. launch {
    8. delay(100L)
    9. 1/0
    10. }
    11. }
    12. delay(1000L)
    13. println("END")
    14. }
    15. Exception in thread "main" java.lang.ArithmeticException: / by zero
    16. at com.example.myapplication.testcoroutinue.TestTryCatchKt$testTryCatch15$1$1$2.invokeSuspend(TestTryCatch.kt:338)
    17. at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    18. at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTask.kt:234)
    19. at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:166)
    20. at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:397)
    21. at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:431)
    22. at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default(CancellableContinuationImpl.kt:420)
    23. at kotlinx.coroutines.CancellableContinuationImpl.resumeUndispatched(CancellableContinuationImpl.kt:518)
    24. at kotlinx.coroutines.EventLoopImplBase$DelayedResumeTask.run(EventLoop.common.kt:494)
    25. at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:279)
    26. at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
    27. at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
    28. at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
    29. at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
    30. at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
    31. at com.example.myapplication.testcoroutinue.TestTryCatchKt.testTryCatch15(TestTryCatch.kt:329)
    32. at com.example.myapplication.testcoroutinue.TestTryCatchKt.main(TestTryCatch.kt:16)
    33. at com.example.myapplication.testcoroutinue.TestTryCatchKt.main(TestTryCatch.kt)
    34. Process finished with exit code 1

    使用CoroutineExceptionHandler 处理上述代码中的异常。

    1. runBlocking {
    2. val coroutineExceptionHandler = CoroutineExceptionHandler { _, throwable ->
    3. println("Catch: $throwable")
    4. }
    5. val coroutineScope = CoroutineScope(coroutineContext + Job() + coroutineExceptionHandler)
    6. coroutineScope.launch {
    7. async {
    8. delay(100L)
    9. }
    10. launch {
    11. delay(100L)
    12. 1 / 0
    13. }
    14. }
    15. delay(1000L)
    16. println("END")
    17. }
    18. Log
    19. Catch: java.lang.ArithmeticException: / by zero
    20. END
    21. Process finished with exit code 0

     定义了一个 CoroutineExceptionHandler,然后把它传入了 scope 当中,就可以捕获其中所有的异常了。

    注意点:在特定场景,为什么 CoroutineExceptionHandler 不起作用?

    1. runBlocking {
    2. val coroutineExceptionHandler = CoroutineExceptionHandler { _, throwable ->
    3. println("Catch: $throwable")
    4. }
    5. val coroutineScope = CoroutineScope(coroutineContext)
    6. coroutineScope.launch {
    7. async {
    8. delay(100L)
    9. }
    10. launch(coroutineExceptionHandler) {
    11. delay(100L)
    12. 1 / 0
    13. }
    14. }
    15. delay(1000L)
    16. println("END")
    17. }
    18. Exception in thread "main" java.lang.ArithmeticException: / by zero
    19. at com.example.myapplication.testcoroutinue.TestTryCatchKt$testTryCatch17$1$1$2.invokeSuspend(TestTryCatch.kt:383)
    20. at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    21. at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTask.kt:234)
    22. at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:166)
    23. at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:397)
    24. at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:431)
    25. at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default(CancellableContinuationImpl.kt:420)
    26. at kotlinx.coroutines.CancellableContinuationImpl.resumeUndispatched(CancellableContinuationImpl.kt:518)
    27. at kotlinx.coroutines.EventLoopImplBase$DelayedResumeTask.run(EventLoop.common.kt:494)
    28. at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:279)
    29. at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
    30. at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
    31. at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
    32. at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
    33. at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
    34. at com.example.myapplication.testcoroutinue.TestTryCatchKt.testTryCatch17(TestTryCatch.kt:371)
    35. at com.example.myapplication.testcoroutinue.TestTryCatchKt.main(TestTryCatch.kt:16)
    36. at com.example.myapplication.testcoroutinue.TestTryCatchKt.main(TestTryCatch.kt)
    37. Process finished with exit code 1

    把自定义的 myExceptionHandler,放到出现异常的 launch 那里传了进去。myExceptionHandler 并不会起作用,异常不会被它捕获。注意:myExceptionHandler 直接定义在发生异常的位置反而不生效,而定义在最顶层却可以生效。因为 只在顶层的协程当中才会起作用。也就是说,当子协程当中出现异常以后,它们都会统一上报给顶层的父协程,然后顶层的父协程才会去调用 CoroutineExceptionHandler,来处理对应的异常。所以需要记住:使用 CoroutineExceptionHandler 处理复杂结构的协程异常,它仅在顶层协程中起作用。

  • 相关阅读:
    软件项目质量管理体系,软件评审,资质认证,安全建设及项目管理匹配资料(原件参考)
    FL STUDIO21正式版升级换代更新
    25G、50G、100G以太网介绍,网络工程师收藏!
    深度学习基础--神经网络(4)参数更新策略,梯度法
    Android、iOS ijkplayer编译步骤及相关问题解决
    Tomcat服务(部署、虚拟主机配置、优化)
    Swift 5.9 有哪些新特性(二)
    ExecutorService、Callable、Future实现有返回结果的多线程原理解析
    安卓开发,拼接屏幕、大屏幕、户外广告无人值守循环播放视频,图片。开机自动播放,断电后自动播放,重起后自动播放功能
    catia距离测量
  • 原文地址:https://blog.csdn.net/zhangying1994/article/details/127610104