Kotlin 协程Flow 流收尾工作 ( finally 代码块收尾 | onCompletion 代码块收尾 | onCompletion 中获取异常信息 | catch 代码块中捕获异常 )
Posted 韩曙亮
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin 协程Flow 流收尾工作 ( finally 代码块收尾 | onCompletion 代码块收尾 | onCompletion 中获取异常信息 | catch 代码块中捕获异常 )相关的知识,希望对你有一定的参考价值。
文章目录
- 一、Flow 流收尾工作
- 二、onCompletion 函数原型
- 三、finally 代码块收尾
- 四、onCompletion 代码块收尾
- 五、onCompletion 代码块中获取异常信息
- 六、catch 代码块中捕获异常
一、Flow 流收尾工作
Flow 流 收集元素 完成后 , 需要进行收尾工作 , 如释放资源等 ;
Flow 流 在执行时可能出现以下两种情况 :
- 收集元素正常执行完成
- 出现异常终止收集元素操作
Flow 流收尾工作可以借助以下方案执行 :
- 在 finally 代码块中进行收尾工作
- 在 onCompletion 代码块中进行收尾
在 onCompletion 代码块中进行收尾 时 , 如果是 因为异常导致 Flow 流收集元素失败 , 则可以 在 onCompletion 代码块中拿到异常信息 ;
二、onCompletion 函数原型
onCompletion 函数原型如下 :
/**
* 在**流完成或取消后,返回一个调用给定[action] **的流
* 作为[action]原因参数的取消异常或失败。
*
* 从概念上讲,' onCompletion '类似于将流集合包装成' finally '块,
* 例如下面的命令代码片段:
*
* ```
* try
* myFlow.collect value ->
* println(value)
*
* finally
* println("Done")
*
* ```
*
* 可以使用' onCompletion '替换为声明性的:
*
* ```
* myFlow
* .onEach println(it)
* .onCompletion println("Done")
* .collect()
* ```
*
* 与[catch]不同,此操作符报告上游和下游都发生的异常
* 并观察为取消流而抛出的异常。异常为空当且仅当
* 流程已经完全成功地完成了。从概念上讲,以下代码:
*
* ```
* myFlow.collect value ->
* println(value)
*
* println("Completed successfully")
* ```
*
* can be replaced with:
*
* ```
* myFlow
* .onEach println(it)
* .onCompletion if (it == null) println("Completed successfully")
* .collect()
* ```
*
* [action]的接收者是[FlowCollector],这个操作符可以用来发出附加操作
* 元素在**结束,如果它成功完成**。例如:
*
* ```
* flowOf("a", "b", "c")
* .onCompletion emit("Done")
* .collect println(it) // prints a, b, c, Done
* ```
*
* 在失败或取消的情况下,任何发出额外元素的尝试都会引发相应的异常。
* 如果需要抑制失败并将其替换为元素的发射,则使用[catch]。
*/
public fun <T> Flow<T>.onCompletion(
action: suspend FlowCollector<T>.(cause: Throwable?) -> Unit
): Flow<T> = unsafeFlow // 注意:这里使用的是不安全流,但安全收集器用于调用完成操作
try
collect(this)
catch (e: Throwable)
/*
* 使用抛掷收集器防止任何排放物从
* 完成顺序时,下游已失败,否则可能
* 使用“finally”导致不可能的非顺序行为
*/
ThrowingCollector(e).invokeSafely(action, e)
throw e
// Normal completion
val sc = SafeCollector(this, currentCoroutineContext())
try
sc.action(null)
finally
sc.releaseIntercepted()
三、finally 代码块收尾
代码示例 :
package kim.hsl.coroutine
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.runBlocking
class MainActivity : AppCompatActivity()
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
runBlocking
try
flowDemo().collect println("收集元素 $it")
finally
println("finally 代码块, 收集元素完毕")
suspend fun flowDemo() = (0..3).asFlow()
执行结果 :
I/System.out: 收集元素 0
I/System.out: 收集元素 1
I/System.out: 收集元素 2
I/System.out: 收集元素 3
I/System.out: finally 代码块, 收集元素完毕
四、onCompletion 代码块收尾
代码示例 :
package kim.hsl.coroutine
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.runBlocking
class MainActivity : AppCompatActivity()
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
runBlocking
flowDemo()
.onCompletion
println("onCompletion 代码块, 收集元素完毕")
.collect
println("收集元素 $it")
suspend fun flowDemo() = (0..3).asFlow()
执行结果 :
I/System.out: 收集元素 0
I/System.out: 收集元素 1
I/System.out: 收集元素 2
I/System.out: 收集元素 3
I/System.out: onCompletion 代码块, 收集元素完毕
五、onCompletion 代码块中获取异常信息
在 onCompletion 代码块中进行收尾 时 , 如果是因为异常导致 Flow 流收集元素失败 , 则可以在 onCompletion 代码块中拿到异常信息 ;
注意 : 在 onCompletion 只是能获取到异常信息 , 并不能捕获该异常 , 程序该崩溃还是崩溃 ;
package kim.hsl.coroutine
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.runBlocking
import java.io.IOException
class MainActivity : AppCompatActivity()
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
runBlocking
flowDemo()
.onCompletion exception ->
if (exception != null)
println("onCompletion 代码块, 收集元素完出现异常 $exception")
.collect
println("收集元素 $it")
suspend fun flowDemo() = flow<Int>
for (i in 0..3)
emit(i)
if (i == 3)
throw IOException("发射元素 IO 异常")
执行结果 :
2022-12-27 10:01:46.472 I/System.out: 收集元素 0
2022-12-27 10:01:46.473 I/System.out: 收集元素 1
2022-12-27 10:01:46.473 I/System.out: 收集元素 2
2022-12-27 10:01:46.473 I/System.out: 收集元素 3
2022-12-27 10:01:46.474 I/System.out: onCompletion 代码块, 收集元素完出现异常 java.io.IOException: 发射元素 IO 异常
2022-12-27 10:01:46.477 D/AndroidRuntime: Shutting down VM
--------- beginning of crash
2022-12-27 10:01:46.483 E/AndroidRuntime: FATAL EXCEPTION: main
Process: kim.hsl.coroutine, PID: 29378
java.lang.RuntimeException: Unable to start activity ComponentInfokim.hsl.coroutine/kim.hsl.coroutine.MainActivity: java.io.IOException: 发射元素 IO 异常
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2951)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3086)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1816)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6718)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Caused by: java.io.IOException: 发射元素 IO 异常
at kim.hsl.coroutine.MainActivity$flowDemo$2.invokeSuspend(MainActivity.kt:33)
at kim.hsl.coroutine.MainActivity$flowDemo$2.invoke(Unknown Source:8)
at kim.hsl.coroutine.MainActivity$flowDemo$2.invoke(Unknown Source:4)
at kotlinx.coroutines.flow.SafeFlow.collectSafely(Builders.kt:61)
at kotlinx.coroutines.flow.AbstractFlow.collect(Flow.kt:212)
at kotlinx.coroutines.flow.FlowKt__EmittersKt$onCompletion$$inlined$unsafeFlow$1.collect(SafeCollector.common.kt:114)
at kim.hsl.coroutine.MainActivity$onCreate$1.invokeSuspend(MainActivity.kt:38)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:277)
at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:87)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:61)
at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source:1)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:40)
at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source:1)
at kim.hsl.coroutine.MainActivity.onCreate(MainActivity.kt:16)
at android.app.Activity.performCreate(Activity.java:7144)
at android.app.Activity.performCreate(Activity.java:7135)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2931)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3086)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1816)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6718)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
2022-12-27 10:01:46.509 I/Process: Sending signal. PID: 29378 SIG: 9
六、catch 代码块中捕获异常
上面章节中介绍了 在 Flow#onCompletion 中可以执行收尾 , 同时可以查看出现的异常 , 但是无法捕获处理异常 ;
在 Flow#catch 代码块中 , 可以直接捕获异常并进行处理 ;
代码示例 :
package kim.hsl.coroutine
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.runBlocking
import java.io.IOException
class MainActivity : AppCompatActivity()
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
runBlocking
flowDemo()
.onCompletion exception ->
if (exception != null)
println("onCompletion 代码块, 收集元素完出现异常 $exception")
.catch exception ->
println("catch 代码块, 捕获到异常 $exception")
.collect
println("收集元素 $it")
suspend fun flowDemo() = flow<Int>
for (i in 0..3)
emit(i)
if (i == 3)
throw IOException("发射元素 IO 异常")
执行结果 :
2022-12-27 10:06:37.168 I/System.out: 收集元素 0
2022-12-27 10:06:37.168 I/System.out: 收集元素 1
2022-12-27 10:06:37.168 I/System.out: 收集元素 2
2022-12-27 10:06:37.168 I/System.out: 收集元素 3
2022-12-27 10:06:37.169 I/System.out: onCompletion 代码块, 收集元素完出现异常 java.io.IOException: 发射元素 IO 异常
2022-12-27 10:06:37.170 I/System.out: catch 代码块, 捕获到异常 java.io.IOException: 发射元素 IO 异常
以上是关于Kotlin 协程Flow 流收尾工作 ( finally 代码块收尾 | onCompletion 代码块收尾 | onCompletion 中获取异常信息 | catch 代码块中捕获异常 )的主要内容,如果未能解决你的问题,请参考以下文章
Kotlin 协程Flow 异步流 ⑥ ( 调用 Flow#launchIn 函数指定流收集协程 | 通过取消流收集所在的协程取消流 )
Kotlin 协程Flow 异步流 ⑥ ( 调用 Flow#launchIn 函数指定流收集协程 | 通过取消流收集所在的协程取消流 )
Kotlin 协程Flow 异步流 ④ ( 流的构建器函数 | flow 构建器函数 | flowOf 构建器函数 | asFlow 构建器函数 )
Kotlin 协程Flow 异步流 ④ ( 流的构建器函数 | flow 构建器函数 | flowOf 构建器函数 | asFlow 构建器函数 )
Kotlin 协程Flow 异步流 ② ( 使用 Flow 异步流持续获取不同返回值 | Flow 异步流获取返回值方式与其它方式对比 | 在 Android 中使用 Flow 异步流下载文件 )
Kotlin 协程Flow 异步流 ② ( 使用 Flow 异步流持续获取不同返回值 | Flow 异步流获取返回值方式与其它方式对比 | 在 Android 中使用 Flow 异步流下载文件 )