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