1.子協程未被捕獲的異常會拋出到父協程,若是父協程沒捕獲異常,會致使父協程終止java
2.launch/actor內發生的未被捕獲的異常不會傳遞到父協程以外的地方android
3.async/produce內發生的未被捕獲的異常會傳遞到啓動其父協程所在的線程bash
4.android原生的try catch沒法捕獲到不一樣線程拋出的異常微信
在協程中,不一樣的啓動方式,對異常的傳播處理不同。async
對於launch和actor構建器是不傳播異常的,async和produce是傳播異常的。 所謂的傳播異常,是指可以將異常主動往外拋到啓動頂層協程所在的線程; 傳播異常表示不在本協程所在線程發生,異常直接往外拋到啓動該協程所在的線程。spa
globalScopeLaunch方式啓動的是頂層協程,自己不存在父協程,在裏面發生異常後, 只會再logCat輸出異常異常,並不會影響到外部線程的運行。線程
fun globalScopeLaunch() {
println("start")
GlobalScope.launch {
println("launch Throwing exception")
throw NullPointerException()
}
Thread.sleep(3000)
//GlobalScope.launch產生的異常不影響該線程執行
println("end")
}
打印出:
start
launch Throwing exception
Exception in thread "DefaultDispatcher-worker-1 @coroutine#1" java.lang.NullPointerException.....
end
複製代碼
globalScopeLaunch方式啓動的是頂層協程,自己不存在父協程,在裏面發生異常後, 並不會影響到外部協程的運行,不管是否運行在同一個線程。code
fun globalScopeLaunch() = runBlocking {
println("start ${Thread.currentThread().id}")
GlobalScope.launch {
println("launch Throwing exception ${Thread.currentThread().id}")
throw NullPointerException()
}.join()
GlobalScope.launch(start = CoroutineStart.UNDISPATCHED) {
println("launch UNDISPATCHED Throwing exception ${Thread.currentThread().id}")
throw IndexOutOfBoundsException()
}.join()
//GlobalScope.launch產生的異常不影響該協程執行
println("end ${Thread.currentThread().id}")
}
打印出:
start 1
launch Throwing exception 11
Exception in thread "DefaultDispatcher-worker-1 @coroutine#2" java.lang.NullPointerException... 異常打印
launch UNDISPATCHED Throwing exception 1
Exception in thread "main @coroutine#1" java.lang.IndexOutOfBoundsException... 異常打印
end 1
複製代碼
async內發生的異常能夠被拋出到啓動該協程所在的線程上,可是須要顯示使用await()方法才能夠捕獲到該異常cdn
fun globalScopeAsync() = runBlocking {
println("start ${Thread.currentThread().id}")
val deferred = GlobalScope.async {
println("async Throwing exception ${Thread.currentThread().id}")
throw NullPointerException()
}
//deferred.join()
deferred.await()
//join不會接收異常能夠正常執行下面步驟,await會接收到拋出的異常致使後面步驟沒法執行
//不使用join和await則能夠正常執行下面步驟
println("end ${Thread.currentThread().id}")
delay(3000)
}
使用join打印出:
start 1
async Throwing exception 11
end 1
使用await打印出:
start 1
async Throwing exception 12
java.lang.NullPointerException....拋出的異常
不使用join和await打印出:
start 1
end 1
async Throwing exception 11
複製代碼
也就意味着不在協程環境下啓動async後,其內部拋出的異常沒法被外部線程捕獲,由於await須要在協程環境下才能調用協程
fun globalScopeAsync() {
println("start")
val deferred = GlobalScope.async {
println("async Throwing exception")
throw NullPointerException()
}
Thread.sleep(3000)
//非協程環境沒法使用await,上邊產生的異常不影響該線程執行
println("end")
}
打印出:
start
async Throwing exception
end
複製代碼
子協程(不管是否與父協程在同一線程,async或launch啓動)未被捕獲的異常都會傳播到父協程,致使父協程終止
fun launchChildException() = runBlocking {
println("start ${Thread.currentThread().id}")
//這邊換成async也是同樣結果
launch {
println("launch Throwing exception ${Thread.currentThread().id}")
throw NullPointerException()
}
delay(3000)
//由於子協程出現異常,默認會致使父協程終止,下邊不會被執行
println("end ${Thread.currentThread().id}")
}
打印出:
start 1
launch Throwing exception 1
java.lang.NullPointerException....
複製代碼
fun launchChildExceptionIO() = runBlocking {
println("start ${Thread.currentThread().id}")
//這邊換成async也是同樣結果
launch(Dispatchers.IO) {
println("launch Throwing exception ${Thread.currentThread().id}")
throw NullPointerException()
}
delay(3000)
//由於子協程出現異常,默認會致使父協程終止,下邊不會被執行
println("end ${Thread.currentThread().id}")
}
打印出:
start 1
launch Throwing exception 11
java.lang.NullPointerException....
複製代碼
對deferred.await()進行try catch能正常捕獲到錯誤, 可是對join try catch等同於沒有。 這也意味着只有async的中發生的異常,才存在被try catch的可能。
fun asyncCatchException() = runBlocking {
println("start ${Thread.currentThread().id}")
val deferred = GlobalScope.async {
println("async Throwing exception ${Thread.currentThread().id}")
throw NullPointerException()
}
try {
//await可以被try catch捕獲錯誤,從而繼續玩下執行
deferred.await()
//join自己忽視錯誤,有沒有try catch都同樣
//deferred.join()
} catch (exception : NullPointerException) {
println("catch exception")
}
println("end ${Thread.currentThread().id}")
delay(3000)
}
await打印出:
start 1
async Throwing exception 11
catch exception
end 1
可是對join打印出:
start 1
async Throwing exception 12
end 1
複製代碼
對於launch這種不傳播異常的協程,try catch沒法捕獲,能夠使用CoroutineExceptionHandler, CoroutineExceptionHandler沒法捕獲async內的異常
fun coroutineExceptionHandler () = runBlocking {
val handleException = CoroutineExceptionHandler { _, throwable ->
//能夠捕獲到launch中拋出的異常,可是不能捕獲async
println("CoroutineExceptionHandler catch $throwable")
}
println("start ${Thread.currentThread().id}")
val job = GlobalScope.launch(handleException) {
println("launch Throwing exception ${Thread.currentThread().id}")
throw NullPointerException()
}
//job.join()
println("end ${Thread.currentThread().id}")
delay(3000)
}
打印出:
launch/launch.join打印出:
start 1
launch Throwing exception 11
CoroutineExceptionHandler catch java.lang.NullPointerException
end 1
複製代碼
CoroutineExceptionHandler沒法捕獲async內的異常,不管有沒調用await()
fun asyncCoroutineExceptionHandler () = runBlocking {
val handleException = CoroutineExceptionHandler { _, throwable ->
//能夠捕獲到launch中拋出的異常,可是不能捕獲async
println("CoroutineExceptionHandler catch $throwable")
}
println("start ${Thread.currentThread().id}")
val referred = GlobalScope.async(handleException) {
println("async Throwing exception ${Thread.currentThread().id}")
throw IndexOutOfBoundsException()
}
// referred.join()
// referred.await()
println("end ${Thread.currentThread().id}")
delay(3000)
}
await打印出:
start 1
async Throwing exception 11
java.lang.IndexOutOfBoundsException...
沒調用await打印:
start 1
async Throwing exception 11
end 1
複製代碼
處於delay的協程再被cancel()後會拋出CancellationException異常,該異常不被捕獲也不會引發父協程的終止,也沒法被CoroutineExceptionHandler捕獲
fun launchCancelException() = runBlocking {
val handleException = CoroutineExceptionHandler { _, throwable ->
//沒法捕獲到launch中拋出的CancellationException異常
println("CoroutineExceptionHandler catch $throwable")
}
println("start ${Thread.currentThread().id}")
launch(handleException) {
println("launch Throwing exception ${Thread.currentThread().id}")
//不會致使父協程終止
throw CancellationException()
}
delay(3000)
//由於子協程出現異常,默認會致使父協程終止,但CancellationException不會
println("end ${Thread.currentThread().id}")
}
打印出:
start 1
launch Throwing exception 1
end 1
複製代碼
可是若是使用了await則異常會拋出在外部線程,須要進行捕獲,不然會致使啓動協程的線程崩潰
fun asyncCancelException() = runBlocking {
val handleException = CoroutineExceptionHandler { _, throwable ->
//沒法捕獲到launch中拋出的CancellationException異常
println("CoroutineExceptionHandler catch $throwable")
}
println("start ${Thread.currentThread().id}")
val deferred = async(handleException) {
println("async Throwing exception ${Thread.currentThread().id}")
//不會致使父協程終止
throw CancellationException()
}
//致使CancellationException異常拋出到該線程,下邊沒法執行
deferred.await()
//若沒有await則下邊能夠正常執行
delay(3000)
println("end ${Thread.currentThread().id}")
}
打印出:
start 1
async Throwing exception 1
end 1
使用了await,打印出:
start 1
async Throwing exception 1
java.util.concurrent.CancellationException..
複製代碼