IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Kotlin学习笔记——(十三)协程异常处理 -> 正文阅读

[移动开发]Kotlin学习笔记——(十三)协程异常处理

注:编码工具为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的子协程都应该设置捕获异常操作。
?????????注意: 只有一级子协程和父协程之间才是主从关系: 单向传播; 而二级及以上子协程与父协程之间没有特殊声明,就是协同关系:双向传播。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-11-28 11:24:56  更:2021-11-28 11:26:28 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 5:20:46-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码