Kotlin協(xié)程之Flow異常示例處理
示例
代碼如下:
launch(Dispatchers.Main) {
// 第一部分
flow {
emit(1)
throw NullPointerException("e")
}.catch {
Log.d("liduo", "onCreate1: $it")
}.collect {
Log.d("liudo", "onCreate2: $it")
}
// 第二部分
flow {
emit(1)
}.onCompletion {
Log.d("liduo", "onCreate3: $it")
}.collect {
Log.d("liudo", "onCreate4: $it")
}
// 第三部分
flow {
emit(1)
throw NullPointerException("e")
}.retryWhen { cause, attempt ->
cause !is NullPointerException && attempt <= 2
}.collect {
Log.d("liudo", "onCreate5: $it")
}
}一.catch方法
catch方法用于捕獲上游流產(chǎn)生的異常,代碼如下:
public fun <T> Flow<T>.catch(action: suspend FlowCollector<T>.(cause: Throwable) -> Unit): Flow<T> =
flow { // 創(chuàng)建Flow對象
// 觸發(fā)上游流的執(zhí)行,并捕獲異常
val exception = catchImpl(this)
// 捕獲到異常,則回調action處理
if (exception != null) action(exception)
}
catch方法是Flow接口的擴展方法,并返回一個Flow類型的對象。在catch方法中,調用flow方法創(chuàng)建了一個Flow對象。
catch方法核心是通過catchImpl方法實現(xiàn)異常的捕獲,如果成功捕獲到異常,則回調參數(shù)action處理。這里參數(shù)action是FlowCollector接口的擴展方法,因此可以繼續(xù)調用emit方法,向下游發(fā)送值。
catchImpl方法
當下游調用collect方法時,會觸發(fā)catch方法創(chuàng)建的Flow對象的執(zhí)行,并調用catchImpl方法來處理,代碼如下:
internal suspend fun <T> Flow<T>.catchImpl(
collector: FlowCollector<T>
): Throwable? {
// 保存下游流執(zhí)行拋出的異常
var fromDownstream: Throwable? = null
try {
// 觸發(fā)上游流的執(zhí)行
collect {
try {
// 將上游流發(fā)送的值作為參數(shù),觸發(fā)下游流執(zhí)行
collector.emit(it)
} catch (e: Throwable) { // 如果下游流在執(zhí)行中發(fā)生異常,保存并拋出
fromDownstream = e
throw e
}
}
} catch (e: Throwable) { // 這里捕獲的異常,可能為上游流的異常——collect方法,
// 也可能為下游流的異?!猠mit方法
// 如果異常是下游流產(chǎn)生的異常,或者是協(xié)程取消時拋出的異常
if (e.isSameExceptionAs(fromDownstream) || e.isCancellationCause(coroutineContext)) {
throw e // 再次拋出,交給下游處理
} else { // 如果是上游流的異常且不為協(xié)程取消異常
return e // 成功捕獲
}
}
// 未捕獲到異常,返回
return null
}
catchImpl方法是Flow接口的擴展方法,因此在調用collect方法時,會觸發(fā)上游流的執(zhí)行。catchImpl方法的核心在于:將上游發(fā)出的值傳遞給下游處理,并對這一過程進行了異常捕獲操作。
二. onCompletion方法
onCompletion方法用于在上游的流全部執(zhí)行完畢后最后執(zhí)行,代碼如下:
public fun <T> Flow<T>.onCompletion(
action: suspend FlowCollector<T>.(cause: Throwable?) -> Unit
): Flow<T> = unsafeFlow { // 創(chuàng)建一個Flow對象
try {
// 觸發(fā)上游流的執(zhí)行
// this表示下游的FlowCollector
collect(this)
} catch (e: Throwable) {// 如果下游發(fā)生異常
// 將異常封裝成ThrowingCollector類型的FlowCollector,并回調參數(shù)action,
ThrowingCollector(e).invokeSafely(action, e)
// 拋出異常
throw e
}
// 如果正常執(zhí)行結束,會走到這里
val sc = SafeCollector(this, currentCoroutineContext())
try {
// 回調執(zhí)行參數(shù)action
sc.action(null)
} finally {
sc.releaseIntercepted()
}
}
onCompletion方法是Flow接口的擴展方法,因此在調用collect方法時,會觸發(fā)上游流的執(zhí)行。同時,傳入this作為參數(shù),this表示下游流調用collect方法時,傳給unsafeFlow方法創(chuàng)建的Flow對象的類型為FlowCollector的對象。onCompletion方法的核心在于:將自身創(chuàng)建的Flow對象作為上游與下游的連接容器,只有當流全部執(zhí)行完畢或執(zhí)行過程中發(fā)生異常,collect方法才可以執(zhí)行完成,繼續(xù)向下執(zhí)行。
1.unsafeFlow方法
unsafeFlow方法用于創(chuàng)建一個類型為Flow對象,與之前在Kotlin協(xié)程:Flow基礎原理提到過的SafeFlow類相比,unsafeFlow方法創(chuàng)建的Flow對象不會對執(zhí)行的上下文進行檢查,代碼如下:
@PublishedApi
internal inline fun <T> unsafeFlow(@BuilderInference crossinline block: suspend FlowCollector<T>.() -> Unit): Flow<T> {
// 返回一個匿名內部類
return object : Flow<T> {
// 回調collect方法是直接執(zhí)行block
override suspend fun collect(collector: FlowCollector<T>) {
collector.block()
}
}
}
雖然onCompletion方法內部使用unsafeFlow方法創(chuàng)建Flow對象,但卻使用了SafeCollector類。根據(jù)之前在Kotlin協(xié)程:Flow基礎原理提到的,調用SafeCollector類的emit方法時,會對上下文進行檢查。因此實際效果與使用SafeFlow類效果相同。
2.ThrowingCollector類
ThrowingCollector類也是一種FlowCollector,用于包裹異常。當調用它的emit方法時,會拋出包裹的異常,代碼如下:
private class ThrowingCollector(private val e: Throwable) : FlowCollector<Any?> {
override suspend fun emit(value: Any?) {
// 拋出異常
throw e
}
}
為什么要重新創(chuàng)建ThrowingCollector對象,而不使用下游的FlowCollector對象呢?
為了防止當下游的流執(zhí)行失敗時,onCompletion方法的action參數(shù)執(zhí)行時調用emit方法發(fā)送數(shù)據(jù),這樣會導致onCompletion方法作為在“finially代碼塊”使用時不是最后執(zhí)行的方法。
onCompletion方法搭配與catch方法,實現(xiàn)try-catch-finially代碼塊的效果。
三. retryWhen方法
retryWhen方法與catch方法類似,都可以用于捕獲上游流產(chǎn)生的異常。但兩者不同之處在于,retryWhen方法還可以根據(jù)“異常類型”和“重試次數(shù)”來決定是否要再次觸發(fā)上游流的執(zhí)行,而且當retryWhen方法不打算再次觸發(fā)上游流的執(zhí)行時,捕獲的異常會被拋出,代碼如下:
// 參數(shù)cause表示捕獲到的異常
// 參數(shù)attempt表示重試的次數(shù)
// 參數(shù)predicate返回true表示重新觸發(fā)上游流的執(zhí)行
public fun <T> Flow<T>.retryWhen(predicate: suspend FlowCollector<T>.(cause: Throwable, attempt: Long) -> Boolean): Flow<T> =
// 創(chuàng)建一個Flow對象
flow {
// 記錄重試次數(shù)
var attempt = 0L
// 表示是否重新觸發(fā)
var shallRetry: Boolean
do {
// 復位成false
shallRetry = false
// 觸發(fā)上游流的執(zhí)行,并捕獲異常
val cause = catchImpl(this)
// 如果捕獲到異常
if (cause != null) {
// 用戶判斷,是否要重新觸發(fā)
if (predicate(cause, attempt)) {
// 表示要重新觸發(fā)
shallRetry = true
// 重試次數(shù)加1
attempt++
} else { // 如果用戶不需要重新觸發(fā)
// 則拋出異常
throw cause
}
}
// 判斷是否重新觸發(fā)
} while (shallRetry)
}
retryWhen方法是Flow接口的擴展方法。retryWhen方法的核心通過catchImpl方法實現(xiàn)對上游流的觸發(fā)及異常捕獲,并加入了由用戶判斷的重試邏輯實現(xiàn)。
到此這篇關于Kotlin協(xié)程之Flow異常示例處理的文章就介紹到這了,更多相關Kotlin Flow異常內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
monkeyrunner環(huán)境搭建及實例教程(3)
這篇文章主要為大家詳細介紹了monkeyrunner環(huán)境搭建及實例教程,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-11-11
Android利用HorizontalScrollView仿ViewPager設計簡單相冊
這篇文章主要介紹了Android利用HorizontalScrollView仿ViewPager設計簡單相冊的相關資料,需要的朋友可以參考下2016-05-05
Android自定義ViewFlipper實現(xiàn)滾動效果
這篇文章主要為大家詳細介紹了Android自定義ViewFlipper實現(xiàn)滾動效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-08-08
Android定時器實現(xiàn)的幾種方式整理及removeCallbacks失效問題解決
本文為大家詳細介紹下Android 定時器實現(xiàn)的幾種方式:Handler + Runnable、Timer的方式、Handle與線程的sleep(long )方法和removeCallbacks失效問題如何解決2013-06-06
深入Android中BroadcastReceiver的兩種注冊方式(靜態(tài)和動態(tài))詳解
這篇文章主要介紹了深入Android中BroadcastReceiver的兩種注冊方式(靜態(tài)和動態(tài))詳解,具有一定的參考價值,有需要的可以了解一下。2016-12-12

