Kotlin Coroutines Flow 系列(三) 異常處理

attractive-beautiful-beauty-face-607891.jpg

五. Flow 異常處理

Flow 可以使用傳統(tǒng)的 try...catch 來捕獲異常:

fun main() = runBlocking {
    flow {
        emit(1)
        try {
            throw RuntimeException()
        } catch (e: Exception) {
            e.stackTrace
        }

    }.onCompletion { println("Done") }
        .collect { println(it) }
}

另外茵臭,也可以使用 catch 操作符來捕獲異常汹胃。

5.1 catch 操作符

上一篇文章Flow VS RxJava2曾講述過 onCompletion 操作符曲伊。

但是 onCompletion 不能捕獲異常谆棱,只能用于判斷是否有異常寡喝。

fun main() = runBlocking {
    flow {
        emit(1)
        throw RuntimeException()
    }.onCompletion { cause ->
        if (cause != null)
            println("Flow completed exceptionally")
        else
            println("Done")
    }.collect { println(it) }
}

執(zhí)行結(jié)果:

1
Flow completed exceptionally
Exception in thread "main" java.lang.RuntimeException
......

catch 操作符可以捕獲來自上游的異常

fun main() = runBlocking {
    flow {
        emit(1)
        throw RuntimeException()
    }
    .onCompletion { cause ->
        if (cause != null)
            println("Flow completed exceptionally")
        else
            println("Done")
    }
    .catch{ println("catch exception") }
    .collect { println(it) }
}

執(zhí)行結(jié)果:

1
Flow completed exceptionally
catch exception

上面的代碼如果把 onCompletion、catch 交換一下位置绷杜,則 catch 操作符捕獲到異常后直秆,不會影響到下游。因此鞭盟,onCompletion 操作符不再打印"Flow completed exceptionally"

fun main() = runBlocking {
    flow {
        emit(1)
        throw RuntimeException()
    }
    .catch{ println("catch exception") }
    .onCompletion { cause ->
        if (cause != null)
            println("Flow completed exceptionally")
        else
            println("Done")
    }
    .collect { println(it) }
}

執(zhí)行結(jié)果:

1
catch exception
Done

catch 操作符用于實現(xiàn)異常透明化處理圾结。例如在 catch 操作符內(nèi),可以使用 throw 再次拋出異常齿诉、可以使用 emit() 轉(zhuǎn)換為發(fā)射值筝野、可以用于打印或者其他業(yè)務邏輯的處理等等。

但是粤剧,catch 只是中間操作符不能捕獲下游的異常歇竟,類似 collect 內(nèi)的異常。

對于下游的異常抵恋,可以多次使用 catch 操作符來解決焕议。

對于 collect 內(nèi)的異常,除了傳統(tǒng)的 try...catch 之外弧关,還可以借助 onEach 操作符盅安。把業(yè)務邏輯放到 onEach 操作符內(nèi),在 onEach 之后是 catch 操作符世囊,最后是 collect()别瞭。

fun main() = runBlocking<Unit> {
    flow {
         ......
    }
    .onEach {
          ......
    }
   .catch { ... }
   .collect()
}

5.2 retry、retryWhen 操作符

像 RxJava 一樣株憾,F(xiàn)low 也有重試的操作符蝙寨。

如果上游遇到了異常,并使用了 retry 操作符,則 retry 會讓 Flow 最多重試 retries 指定的次數(shù)籽慢。

public fun <T> Flow<T>.retry(
    retries: Long = Long.MAX_VALUE,
    predicate: suspend (cause: Throwable) -> Boolean = { true }
): Flow<T> {
    require(retries > 0) { "Expected positive amount of retries, but had $retries" }
    return retryWhen { cause, attempt -> attempt < retries && predicate(cause) }
}

例如,下面打印了三次"Emitting 1"猫胁、"Emitting 2"箱亿,最后兩次是通過 retry 操作符打印出來的。

fun main() = runBlocking {

    (1..5).asFlow().onEach {
        if (it == 3) throw RuntimeException("Error on $it")
    }.retry(2) {

        if (it is RuntimeException) {
            return@retry true
        }
        false
    }
    .onEach { println("Emitting $it") }
    .catch { it.printStackTrace() }
    .collect()
}

執(zhí)行結(jié)果:

Emitting 1
Emitting 2
Emitting 1
Emitting 2
Emitting 1
Emitting 2
java.lang.RuntimeException: Error on 3
......

retry 操作符最終調(diào)用的是 retryWhen 操作符弃秆。下面的代碼跟剛才的執(zhí)行結(jié)果一致:

fun main() = runBlocking {

    (1..5).asFlow().onEach {
        if (it == 3) throw RuntimeException("Error on $it")
    }
    .onEach { println("Emitting $it") }
    .retryWhen { cause, attempt ->
        attempt < 2
    }
    .catch { it.printStackTrace() }
    .collect()
}

因為 retryWhen 操作符的參數(shù)是謂詞届惋,當謂詞返回 true 時才會進行重試。謂詞還接收一個 attempt 作為參數(shù)表示嘗試的次數(shù)菠赚,該次數(shù)是從0開始的脑豹。

六. Flow Lifecycle

RxJava 的 do 操作符能夠監(jiān)聽 Observables 的生命周期的各個階段。

Flow 并沒有多那么豐富的操作符來監(jiān)聽其生命周期的各個階段衡查,目前只有 onStart瘩欺、onCompletion 來監(jiān)聽 Flow 的創(chuàng)建和結(jié)束。

fun main() = runBlocking {

    (1..5).asFlow().onEach {
        if (it == 3) throw RuntimeException("Error on $it")
    }
    .onStart { println("Starting flow") }
    .onEach { println("On each $it") }
    .catch { println("Exception : ${it.message}") }
    .onCompletion { println("Flow completed") }
    .collect()
}

執(zhí)行結(jié)果:

Starting flow
On each 1
On each 2
Flow completed
Exception : Error on 3

例舉他們的使用場景:
比如拌牲,在 Android 開發(fā)中使用 Flow 創(chuàng)建網(wǎng)絡(luò)請求時俱饿,通過 onStart 操作符調(diào)用 loading 動畫以及網(wǎng)絡(luò)請求結(jié)束后通過 onCompletion 操作符取消動畫。

再比如塌忽,在借助這些操作符做一些日志的打印拍埠。

fun <T> Flow<T>.log(opName: String) = onStart {
    println("Loading $opName")
}.onEach {
    println("Loaded $opName : $it")
}.onCompletion { maybeErr ->
    maybeErr?.let {
        println("Error $opName: $it")
    } ?: println("Completed $opName")
}

該系列的相關(guān)文章:

Kotlin Coroutines Flow 系列(一) Flow 基本使用
Kotlin Coroutines Flow 系列(二) Flow VS RxJava2
Kotlin Coroutines Flow 系列(四) 線程操作
Kotlin Coroutines Flow 系列(五) 其他的操作符

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市土居,隨后出現(xiàn)的幾起案子枣购,更是在濱河造成了極大的恐慌,老刑警劉巖擦耀,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件棉圈,死亡現(xiàn)場離奇詭異,居然都是意外死亡埂奈,警方通過查閱死者的電腦和手機迄损,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來账磺,“玉大人芹敌,你說我怎么就攤上這事】蹇梗” “怎么了氏捞?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長冒版。 經(jīng)常有香客問我液茎,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任捆等,我火速辦了婚禮滞造,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘栋烤。我一直安慰自己谒养,他們只是感情好,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布明郭。 她就那樣靜靜地躺著买窟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪薯定。 梳的紋絲不亂的頭發(fā)上始绍,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天,我揣著相機與錄音话侄,去河邊找鬼亏推。 笑死,一個胖子當著我的面吹牛满葛,可吹牛的內(nèi)容都是我干的径簿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼嘀韧,長吁一口氣:“原來是場噩夢啊……” “哼篇亭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起锄贷,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤译蒂,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后谊却,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體柔昼,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年炎辨,在試婚紗的時候發(fā)現(xiàn)自己被綠了捕透。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡碴萧,死狀恐怖乙嘀,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情破喻,我是刑警寧澤虎谢,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站曹质,受9級特大地震影響婴噩,放射性物質(zhì)發(fā)生泄漏擎场。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一几莽、第九天 我趴在偏房一處隱蔽的房頂上張望迅办。 院中可真熱鬧,春花似錦章蚣、人聲如沸礼饱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至匀伏,卻和暖如春洒忧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背够颠。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工熙侍, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人履磨。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓蛉抓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親剃诅。 傳聞我的和親對象是個殘疾皇子巷送,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容