kotlin之flow 理解

之前介绍的启动协程方法,比如 launch、async 都是协程的单次启动。如果有复杂场景,比如发送多个数据,就需要使用 flow 数据流。在 flow 中,数据如水流一样经过上游发送,中间站处理,下游接收。

创建 flow 有 3 种方式:

flow{}

flowOf()

asFlow()


1.flow{} 中使用 emit 发送数据。

fun flowEmit() = runBlocking {

    flow {

        emit(1)

        emit(2)

        emit(3)

        emit(4)

        emit(5)

    }

        .filter {

            it > 2

        }

        .map {

            it * 2

        }

        .take(2)

        .collect {

            // 6 8

            println(it)

        }

}


2.flowOf

flowOf() 可以将指定的一串数据转换为 flow,接收可变参数。

fun flowOfFun() = runBlocking {

    flowOf(1, 2, 3, 4, 5)

        .filter { it > 2 }

        .map { it * 2 }

        .take(2)

        .collect {

            // 6 8

            println(it)

        }

    listOf(1, 2, 3, 4, 5)

        .filter { it > 2 }

        .map { it * 2 }

        .take(2)

        .forEach {

            // 6 8

            println(it)

        }

}

3.asFlow

asFlow() 可以将 List 集合转换为 flow。toList() 可以将 flow 转换为 List 集合。

fun flow2list() = runBlocking {

    flowOf(1, 2, 3, 4, 5)

        // flow to list

        .toList()

        .filter { it > 2 }

        .map { it * 2 }

        .take(2)

        .forEach {

            println(it)

        }

    listOf(1, 2, 3, 4, 5)

        // list as flow

        .asFlow()

        .filter { it > 2 }

        .map { it * 2 }

        .take(2)

        .collect {

            println(it)

        }

}

4.中间操作符

创建 flow 之后使用中间操作符处理 flow 的每一个数据。flow 的中间操作符和 list 集合的操作符非常类似。

常用中间操作符:

filter

filter 传入判断条件,条件满足时过滤数据,否则不将数据流向下游。

map

map 传入映射函数,将每个数据传入映射函数,得到结果继续传入下游。

take

take 传入非负整数 n,取前 n 个数据传入下游。

5.终止操作符

collect 是 flow 的终止操作符,收集每一个数据经过中间操作符后的最终结果,表示 flow 流的终止,后面不能再调用中间操作符。

除了 collect,还有一些其他的终止操作符,first、single、fold、reduce。

1)collect

返回所有元素,结束 flow。

2)first

返回第一个元素,结束 flow。

3)single 

返回唯一元素,结束flow。不能多于一个,也不能一个没有。

4).fold

折叠所有元素。指定一个函数和初始值,对每一个元素反复执行函数,返回最后的结果。

5)reduce

reduce 和 fold 很类似,reduce 没有初始值。

first、single、fold、reduce 本质都是封装了 collect ,因此它们都是终止操作符。


6.onStart 是 flow 的开始生命周期回调。onStart 的执行时机和它在 flow 位置无关。

7.onComplete

flow 执行完后回调 onComplete。onComplete 的执行时机和它在 flow 的位置无关。

flow 正常执行完回调 onComplete。

8.异常处理

flow 的异常处理可以分为上游异常和下游异常。上游异常指创建 flow 或者中间操作符发生的异常。下游异常指终止操作符 collect 发生的异常。

上游异常

上游异常可以用 catch 函数捕获异常。catch 函数和它的位置相关,只能捕获 catch 上游的异常。

下游异常不能用 catch 函数,需要在 collect 的作用域用 try-catch 捕获。

catch 函数无法捕获下游的 filter 除 0 异常。

9.线程切换

1)flowOn 可以指定上游所有操作符运行的线程,和它的位置相关。

collect 运行在 main 线程,上游运行在 IO 线程,指定 DefaultDispatcher。

flowOn 在 filter 之前,emit 执行在 IO 线程,filter 和 collect 执行在 main 线程。

因为 flowOn 只能用于上游,在 collect 可以用 withContext 切换线程,但不建议这么用。

collect 运行在 DefaultDispatcher,其他运行在 main 线程。

flow 的 emit、filter、collect 都运行在 DefaultDispatcher。

2)launchIn

flow 提供了 launchIn 函数指定在哪个线程执行。launchIn 运行在指定的 CoroutineScope。

flowOn 之前的运行在 Dispatchers.IO,下游运行在 launchIn 指定的 scope。

launchIn 调用了 scope 的 launch,然后执行 collect。相当于终止操作符。

10.flow 是冷的

flow 是冷的,只有接收者存在的情况下才会发送数据。如果不调用 collect,emit 不会执行。相反 channel 是热的,不管有没有接收者都会发送。

flow 的 emit 未执行。


总结

flow 是 kotlin 提供的解决复杂异步场景的方案。

flow 由创建、中间操作符、终止操作符三个部分组成。

flow 的生命周期可以分为 onStart 和 onComplete,与它们在 flow 的位置无关。

flow 的异常处理使用 catch。catch 与位置相关。

flow 的线程切换使用 flowOn 和 launchIn。flowOn 控制上游,launchIn 控制全局。

flow 是冷的,只有存在接收者它才会开始执行。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,311评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,339评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,671评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,252评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,253评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,031评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,340评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,973评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,466评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,937评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,039评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,701评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,254评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,259评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,497评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,786评论 2 345