参考链接

示例来自bilibili Kotlin语言深入解析 张龙老师的视频

1  一个方法返回多个结果各种实现

/*** 一个方法返回多个结果* 方式1 返回一个集合* 特点* 1 方法本身是阻塞的,即主线程会进入该方法内部执行,一直执行到方法结束* 2 集合本身是一次性返回给调用者的,即集合中的全部元素均已经获得之后才统一返回给调用端*/
private fun myMethod(): List<String> = listOf("hello", "world", "welcome")fun main() {myMethod().forEach { println(it) }
}class HelloKotlin1 {
}/*** 一个方法返回多个结果* 方式2 返回一个序列(sequence)* 如果在获取每一个结果时都需要执行一定的计算,这种计算是一种阻塞行为,每计算出一个结果就立即返回给调用端* 特点* 1 序列中的数据并不像集合那样一次性返回,而是计算完一个数据后就返回* 2 序列中的计算过程会占用主线程执行,因此会阻塞主线程*/
private fun myMethod(): Sequence<Int> = sequence {for (i in 100..105) {Thread.sleep(100)yield(i)}
}fun main() {myMethod().filter { it % 2 == 0 }.map { it -> "result: $it" }.forEach { println(it) }
}class HelloKotlin2 {
}/*** 一个方法返回多个结果* 方式3 返回一个集合(结合协程)* 特点* 1 不会阻塞主线程* 2 集合本身是一次性返回给调用者的*/
private suspend fun myMethod(): List<String> {delay(100)return listOf("hello", "world", "welcome")
}fun main() = runBlocking {myMethod().forEach { println(it) }
}class HelloKotlin3 {
}/*** 一个方法返回多个结果* 方式4 返回一个Flow** 如果返回一个List<String> 那么代表只能一次性返回所有的值。要想能够像sequence一样一次返回一个值* 并且还不会阻塞主线程,可以使用Flow<String>类型** 特点* 1 Flow的创建使用flow构建器* 2 位于flow构建器中的代码是可以挂起的 而不需要额外的suspend关键字* 3 使用emit函数发射函数* 4 Flow里面的值通过collect来收集** flow简单介绍* flow是一种冷异步数据流,它按顺序发出值,正常或异常完成** flow的构建器有如下几种* flowOf(...) functions to create a flow from a fixed set of values.* asFlow() extension functions on various types to convert them into flows.* flow { ... } builder function to construct arbitrary flows from sequential calls to emit function.* channelFlow { ... } builder function to construct arbitrary flows from potentially concurrent calls to the send function.** flow有中间操作符 如map、filter、take、zip 等* 中间操作不会执行流程中的任何代码,也不会挂起函数本身* flow还有终端操作符 flow上的终端操作符要么是暂停函数,如 collect、single、reduce、toList 等,* 要么是启动给定范围内流的收集的 launchIn 操作符**/private fun myMethod(): Flow<Int> = flow {for (i in 1..4) {delay(100)//Thread.sleep(100) //sleep1emit(i)}
}fun main() = runBlocking<Unit> {launch {for (i in 1..4) {println("hello in launch $i")delay(200)//Thread.sleep(100) //sleep2// sleep方法不管加在那里 都会导致并行运行失效 因为Thread.sleep(100)需要睡眠主线程// 因此加了sleep的方法就类似变成了非挂起函数}}myMethod().collect {println(it)}// 这里launch里面的输出和myMethod().collect并行运行
}class HelloKotlin4 {
}

2  Flow的执行

/*** Flow的执行* * Flow 只有执行了终端操作符之后(例如collect) flow才会真正执行* 例如下面 我们调用myMethod方法后 myMethod立即返回 但是其中的代码不会运行*/
private fun myMethod(): Flow<Int> = flow {println("myMethod execute")for (i in 1..4) {delay(100)emit(i)}
}fun main() = runBlocking {println("enter")val flow = myMethod()//只调用这个方法 不会执行myMethod内部的内容println("1111")flow.collect {println(it)}println("2222")
}class HelloKotlin5 {
}

3  Flow的取消

/*** Flow的取消* Flow实际上没有提供取消的方法 Flow的取消依赖于协程,如果Flow依附的协程取消了,则Flow也会取消** Flow的收集操作是可以取消的,前提是Flow在一个可以取消挂起函数(如delay)中被挂起了,除此之外,我们无法* 通过其他方式取消Flow*/private fun myMethod(): Flow<Int> = flow{for (i in 1..4){delay(100)println("emit $i")emit(i)}
}fun main() = runBlocking {// 协程超过280毫秒会被取消withTimeoutOrNull(280) {// 如果改成withTimeout会抛出TimeoutCancellationExceptionmyMethod().collect {println(it)}}println("finished")
}class HelloKotlin6 {
}

4 Flow builder(流构建器)

/*** Flow builder(流构建器)* 1 flow构建器 是经常被使用的流构建器* 2 flowOf构建器 可以用于定义能够发射固定数量值的流* 3 asFlow构建器 对于各种集合与序列来说,他们都提供了asFlow()扩展方法来将自身转换为Flow**** 非局部返回* inline,noinline,crossinline* non-local reutrns:非局部返回 实际上表示的是在一个方法内部,我们可以在其中通过一个lambda表达式的返回来直接将外层方法返回返回* crossinline的作用实际上表示被标记的lambda是禁止非局部返回?*/fun main() = runBlocking {// asFlow构建器可以将各种集合转为Flow 例如 LongRange IntRange IntArray Array<T> Sequence<T>(1..10).asFlow().collect {println(it)}println("-------")// flowOf构建器可以用于定义能够发射固定数量值 flowOf的参数是一个可变参数flowOf(10,20,3,4,50).collect {println(it)}
}class HelloKotlin7 {
}

5 Flow 的中间操作符filter map

/*** Flow 的中间操作符filter map* Sequence也可以调用filter map等方法* Flow与Sequence之间的区别:对于Flow来说 中间运算符代码内可以调用挂起函数(对比Kotlin2)*/
private suspend fun myExecution(input: Int): String {println("myExecution: $input")delay(500)return "output: $input"
}fun main() = runBlocking<Unit> {// 将1到10转换为flow 过滤其中大于5的数字 并将这些数字形成一个映射 最后将其打印出来(1..10).asFlow().filter { it > 5 }.map { it -> myExecution(it) }// 这里中间函数map调用了挂起函数.collect { println(it) }
}class HelloKotlin8 {
}

6 Flow 的中间运算符 transform

/*** Flow 的中间运算符 transform* transform内部可以随意处理每一个Flow元素 理论上 transform可以实现其他任意的中间操作符或者他们的组合** Applies transform function to each value of the given flow.* The receiver of the transform is FlowCollector and thus transform is a flexible function that may transform emitted element, skip it or emit it multiple times.* This operator generalizes filter and map operators and can be used as a building block for other operators, for example:** 将变换函数应用于给定Flow的每个值。* 变换的接收者是 FlowCollector,因此变换是一个灵活的函数,可以变换发射的元素,跳过它或多次发射它。* 此运算符概括了过滤器和映射运算符,可用作其他运算符的构建块*/
private suspend fun myExecution(input: Int): String {delay(500)return "output: $input"
}fun main() = runBlocking {(1..10).asFlow().transform { input ->if (input > 5){// transform可以让flow多次发射值 collect都能收集到emit("my input: $input")emit(myExecution(input))emit("hello")}// input为输入  emit的内容为输出}.collect { println(it) }
}
/*
输出
my input: 6
output: 6
hello
my input: 7
output: 7
hello
my input: 8
output: 8
hello
my input: 9
output: 9
hello
my input: 10
output: 10
helloProcess finished with exit code 0*/
class HelloKotlin9 {
}

7 Flow 的中间运算符 take

/*** Flow 的中间运算符 take* 它本质是使用Kotlin的异常来实现只取出限定数量的数据‘** 限定处理发射数量的中间操作符 当超出限制数量 会抛出异常* Flow<T>.take方法可以限制指定数量的flow中的发射的元素** Returns a flow that contains first count elements.* When count elements are consumed, the original flow is cancelled.* Throws IllegalArgumentException if count is not positive.* 返回包含第一个 count 元素的流。* 当 count 元素被消耗时,原始流被取消。* 如果 count 不是正数,则抛出 IllegalArgumentException。*/
private fun myNumbers(): Flow<Int> = flow {try {emit(1)emit(2)println("middle")emit(3)emit(4)} catch (ex: Exception) {println(ex)} finally {println("finally")}
}fun main() = runBlocking {myNumbers().take(2).collect {println(it)}
}
/*
输出
1
2
kotlinx.coroutines.flow.internal.AbortFlowException: Flow was aborted, no more elements needed
finallyProcess finished with exit code 0*/class HelloKotlin10 {
}

8 Flow的终端操作符(Terminal Operation)

/*** Flow的终端操作符(Terminal Operation)* Flow的终止操作符都是挂起函数 终止操作才会真正开始流的收集** 主要终端操作符* 1 除了collect 其他终端操作符 toList toSet* 2 只获取第一个元素* 3 reduce 将一系列值合成一个单个的值* Accumulates value starting with the first element and applying operation to current accumulator value and each element.* Throws NoSuchElementException if flow was empty.* 从第一个元素开始累加值,并将操作应用于当前累加器值和每个元素。* 如果流为空,则抛出 NoSuchElementException。*/fun main() = runBlocking {val result = (1..4).asFlow().reduce { a, b -> a+b }println(result)val result1 = (1..4).asFlow().toList()println(result1)val result2 = (1..4).asFlow().toSet()println(result2)
}
class HelloKotlin11 {
}

9 Flow 默认是顺序执行的

/*** Flow 默认是顺序执行的* 对于Flow的收集来说,它是运行在调用了终止操作符的那个协程上。默认情况下,它不会启动新的协程。* 每个emit的元素值都会由所有的中间操作进行处理,最后再由终止操作进行处理。本质上就是每一个元素* 依次执行 中间操作 终端操作,这样一个一个顺序执行*/fun main() = runBlocking<Unit> {(1..10).asFlow().filter {// 有的元素在第一步中就被过滤了println("第一步 filter: $it")it % 2 == 0}.map {println("第二步 map: $it")"Hello: $it"}.collect {println("第三步 final: $it")println("=============")}
}class HelloKotlin12 {
}

10 Flow Context

/*** Flow Context(Flow 上下文)* Flow的收集总是发生在调用协程的上下文中,这个特性叫做上下文保留(Context Preservation)* 注意加上-Dkotlinx.coroutines.debug*/
private fun log(logMessage: String) = println("[${Thread.currentThread().name}] $logMessage")private fun myMethod(): Flow<Int> = flow {log("start")for (i in 1..4) {emit(i)}
}fun main() = runBlocking {// 例如下面的Flow的收集操作发生在runBlocking的上下文 即主线程中myMethod().collect {log("Collected: $it")}
}/*
输出[main @coroutine#1] start
[main @coroutine#1] Collected: 1
[main @coroutine#1] Collected: 2
[main @coroutine#1] Collected: 3
[main @coroutine#1] Collected: 4*/class HelloKotlin13 {
}

11 flowOn 切换Flow上下文

/*** 尝试切换Flow上下文*/
private fun myMethod(): Flow<Int> = flow {// 尝试将Flow的发射上下文更改withContext(Dispatchers.Default){for (i in 1..4) {emit(i)}}
}fun main() = runBlocking {myMethod().collect {println(it)}
}
/*
输出Exception in thread "main" java.lang.IllegalStateException: Flow invariant is violated:Flow was collected in [BlockingCoroutine{Active}@76aa9e23, BlockingEventLoop@5fcb12cd],but emission happened in [DispatchedCoroutine{Active}@1f1d9c3, Dispatchers.Default].Please refer to 'flow' documentation or use 'flowOn' instead
报错原因 违反了Flow的不变性
Flow收集发生在[BlockingCoroutine{Active}@76aa9e23, BlockingEventLoop@5fcb12cd]
Flow发射发生在[DispatchedCoroutine{Active}@1f1d9c3, Dispatchers.Default]
要实现这样的需求需要使用flowOn代替*/
class HelloKotlin14 {
}/*** flowOn 切换Flow上下文** 借助flowOn,可以让Flow在发射元素所在的上下文与收集(终端操作符)所处的上下文是不同的。* 值得注意的是:flowOn运算符改变了Flow的顺序性。* 现在 收集操作发生在一个协程上,而发射操作发射在另外的协程* flowOn运算符本质上会改变上下文中的CoroutineDispatcher,并且为上游的flow创建另外一个协程* 上游可以简单理解为发生在当前操作的前面 不过 终端操作即使发生在中间操作之前,也不能称之为上游** 注意加上-Dkotlinx.coroutines.debug*/private fun log(logMessage: String) = println("[${Thread.currentThread().name}] $logMessage")private fun myMethod(): Flow<Int> = flow {for (i in 1..4) {Thread.sleep(100)log("emit: $i")emit(i)}
}.flowOn(Dispatchers.Default)// 对比14 的差异 在这里切换emit的上下文fun main() = runBlocking {myMethod().collect {value -> log("Collected $value")}
}
/*
输出
[DefaultDispatcher-worker-1 @coroutine#2] emit: 1
[main @coroutine#1] Collected 1
[DefaultDispatcher-worker-1 @coroutine#2] emit: 2
[main @coroutine#1] Collected 2
[DefaultDispatcher-worker-1 @coroutine#2] emit: 3
[main @coroutine#1] Collected 3
[DefaultDispatcher-worker-1 @coroutine#2] emit: 4
[main @coroutine#1] Collected 4可以看到Flow的发射与收集工作在不同的协程*/class HelloKotlin15 {}

Kotlin学习笔记28 Flow part2 Flow引入 Flow的执行 取消 构建器 中间操作符 终端操作符 默认执行顺序 上下文相关相关推荐

  1. Kotlin学习笔记29(完结篇) Flow part2 Flow的Buffer 中间操作符zip 打平 异常处理 Flow的完成 onCompletion的优势 onCompletion陷阱

    参考链接 示例来自bilibili Kotlin语言深入解析 张龙老师的视频 1  Buffer 缓冲 /*** Buffer 缓冲* 这里没有使用缓冲*/private fun myMethod() ...

  2. Kotlin 学习笔记(二)—— 数据类、密闭类、循环写法以及常用集合操作符

    在上篇笔记中,我们对 Kotlin 的基本类型.关键字.类与对象,以及与 Java 之间互调的内容有了一些认识,这篇笔记来看看 Kotlin 中几种特殊的类,以及集合相关的常用操作. 1. Kotli ...

  3. Kotlin学习笔记18 反射Part2

    参考链接 示例来自bilibili Kotlin语言深入解析 张龙老师的视频 12 通过KClass获取泛型类型参数 /*** 通过KClass获取泛型类型参数*/class MyTestClass& ...

  4. CMU15-213学习笔记(六)Exceptional Control Flow

    CMU15-213学习笔记(六)Exceptional Control Flow 程序的正常执行顺序有两种: 按顺序取下一条指令执行 通过CALL/RET/Jcc/JMP等指令跳转到转移目标地址处执行 ...

  5. 小猫爪:i.MX RT1050学习笔记16-启动流程(Boot Flow)

    小猫爪:i.MX RT1050学习笔记16-启动流程(Boot Flow) 1 前言 2 bootROM的流程 2.1低功耗唤醒启动 2.2 正常启动 2.2.1 Serial Download 2. ...

  6. Kotlin 学习笔记(七)—— Kotlin类与对象之属性与字段

    Kotlin 学习笔记(七)-- Kotlin类与对象之属性与字段 Kotlin学习笔记系列教程 Kotlin 学习笔记(一)-- 概述.学习曲线.开发工具.参考资料 Kotlin 学习笔记(二)-- ...

  7. Kotlin学习笔记12——数据类和密封类

    Kotlin学习笔记12--数据类和密封类 前言 数据类 在类体中声明的属性 复制 componentN 解构声明 密封类 尾巴 前言 上一篇,我们学习了Kotlin中的拓展,今天继续来学习Kotli ...

  8. Kotlin 学习笔记(十四)浅读协程

    上一篇-Kotlin 学习笔记(十三)高阶函数 为什么需要协程   举例一个异步编程中最常见的场景:后台线程执行一个A任务,下一个B任务依赖于A任务的执行结果,所以必须等待上一个任务执行完成后才能开始 ...

  9. Python学习笔记28:从协议到抽象基类

    Python学习笔记28:从协议到抽象基类 今后本系列笔记的示例代码都将存放在Github项目:https://github.com/icexmoon/python-learning-notes 在P ...

最新文章

  1. arduino nano 蓝牙_探索 Golang 云原生游戏服务器开发,5 分钟上手 Nano 游戏服务器框架...
  2. 数组去重的正确编写姿势
  3. 关于Laravel中使用response()方法调用json()返回数据unicode编码转换的问题解决
  4. ThinkPHP框架 _ 学习3
  5. jsp value设置为函数的返回值_python中的生成器函数是如何工作的?
  6. f5源站获取http/https访问的真实源IP解决方案
  7. JAVA和JVM运行原理揭秘
  8. uniapp 在HBuilder X中配置微信小程序开发工具
  9. Fovea Box阅读学习笔记
  10. 市面上哪款输入法最好用,对比出结论
  11. hdu 5064 Find Sequence
  12. 6款免费网络延迟测试工具
  13. 同心拼图(concentric mosaics)
  14. 采蘑菇电脑c语言,英菲尼迪终于升级英菲尼迪Q50L,内行人告诉你怎么选还配备主动降噪、胎压显示!凯美瑞都比不上它! 早买早享受...
  15. 网络游戏开发成本,运营成本,收入
  16. oracle在linux自启动和停止脚本
  17. 分析linux启动内核源码
  18. 天津工业大学软件园 ubuntu电信网设置。
  19. 如何通过结构分析法分析数据?
  20. 微信小程序积分商城接入兑吧

热门文章

  1. PHP中如何判断属性类型,php – 如何获取doctrine实体属性的类型
  2. getchwd() 函数返回当前工作目录。
  3. ASP.NET MVC 中@Html.Partial,@Html.Action,@Html.RenderPartial,@Html.RenderAction
  4. 移动web:转盘抽奖(幸运大转盘)
  5. Excel与DataGridView的操作示例
  6. python运行错误总结(按字母序)
  7. 计算机中常用术语CAD是指,计算机基础知识理论复习题及答案
  8. CCF201712-1 最小差值
  9. 贪心策略——部分背包问题
  10. 深度优先搜索——全排列(洛谷 P1706)