<
协程 与 异常
>
上一篇

Coroutine `supervisorjob` 和 `job` 的区别
下一篇

Android 中 lifecyclescope viewmodelscope 协程 上下文

协程 与 异常


若子协程抛出异常,只有外层父协程能 handle 到异常,中间层的协程 handle 不到,若外层父协程不 handle , 程序会报错

协程内抛出的异常可以通过传入 CoroutineExceptionHandler 对象去处理,操作符 + 可处理 上下文 和 handler

val handler = CoroutineExceptionHandler { context, throwable ->
	toast("未正常支付")
	LogUtils.e("${throwable.message}")
}
lifecycleScope.launch(lifecycleScope.coroutineContext + handler) {
	withContext(Dispatchers.IO) {
		PayTask(this@VipActivity).payV2(orderInfo, true)
	}.let {
		when (it["resultStatus"]) {
			"9000" -> {
				toast("支付成功")
				refreshPageAfterVip()
			}
			"6001" -> {
				toast("取消支付")
			}
			else -> {
				toast("未正常支付")
			}
		}
	}
}

JobSupervisorJob 的一个区别是,
Job 的子协程发生异常被取消会同时取消 Job 的其它子协程,
SupervisorJob 不会。(含有 SupervisorJob SupervisorCoroutine 或 重写 childCancelled 函数且返回 false 等情况的)

查看代码 Job 实例化

public fun Job(parent: Job? = null): CompletableJob = JobImpl(parent)

查看代码 SupervisorJob 实例化

public fun SupervisorJob(parent: Job? = null) : CompletableJob = SupervisorJobImpl(parent)

再查看,发现原因是重写方法 childCancelled 返回 false

private class SupervisorJobImpl(parent: Job?) : JobImpl(parent) {
    override fun childCancelled(cause: Throwable): Boolean = false
}

launch 和 async 是异步, 理论上在代码块外是捕获不到异常
但 saync 搭配 await(同步), 可以在 await 捕获异常
runBlocking withContext 在里外都可以捕获异常

runBlocking 可以在block外面 catch

try {
    runBlocking(lifecycleScope.coroutineContext) {
        throw RuntimeException("test")
    }
} catch (e: Exception) {
    Log.e("runBlocking", "catch Exception ${e.message}")
}

launch 必须在block里面 catch

val job = launch(lifecycleScope.coroutineContext) {
    try {
        throw RuntimeException("test")
    } catch (e: Exception) {
        Log.e("launch", "catch Exception ${e.message}")
    }
}
job.join()

withContext 可以在block外面 catch

try {
    val result = withContext(lifecycleScope.coroutineContext) {
        throw RuntimeException("test")
    }
} catch (e: Exception) {
    Log.e("withContext", "withContext Exception ${e.message}")
}

async 可以在block外面 catch,具体是 await 时

try {
    val deferred = async(lifecycleScope.coroutineContext) {
        throw RuntimeException("test")
    }
    val result = deferred.await()
} catch (e: Exception) {
    Log.e("async", "catch Exception ${e.message}")
}
Top
Foot