day17 不懂kotlin flow资料流? 那喝杯进口奶茶吧

用过Rx或reactive stream的大大,应该会很好理解flow,从设计概念来讲,flow也属於react stream,如果有从那边转过来的人,可以先看这篇由jetBrain Project Lead写的文章,谈到了Reactive Streams and Kotlin Flows

对新手而言,要讲flow就得先讲讲英文,去看剑桥字典flow大概是持续、不断的流动的意思,再看看文档
A suspending function asynchronously returns a single value, but how can we return multiple asynchronously computed values? This is where Kotlin Flows come in.
对,开头就讲到suspend只会返回一个值。但为了能够返回多的非同步的result,就要用flow

另一句文档的描述是,透过flow建构器 建构 带 全面操作符 的冷的非同步流
Flow — cold asynchronous stream with flow builder and comprehensive operator set (filter, map, etc);

因为我中文不好,这边拆一下,

  1. 他必须透过flow builder建构
  2. 有很多操作符可以用
  3. 被定义为冷的非同步流

至於为什麽分冷热?之後会再开一篇讲flow、stateFlow、sharedFlow和channel的差异,这里一样先专注讲flow是什麽,那做为一个正常的新手,对flow概念应该还是,我听你讲了很多,但我还是没听懂

这其实很正常,当初我直接看文档,也是看不懂,有种被忽悠的感觉,一个字一个字都懂,放在一起就不懂了,在看图之前,我想用一个例子解释一下flow,进口奶茶

原本的连结,如果有校稿者,这边以base64的方式崁入图片是可以的吗?

我也没想到我想了好几周的最好例子会是这个
以这个范例来说,男方是consumer, 女方是intermediary,奶茶是producer,奶茶并不关心是谁喝他,也不在意最後男方喝到什麽东西,奶茶只负责给奶茶

换个角度,男方其实也不在意奶茶给了甚麽,看他的眼神和女方深情对望就知道,女方给甚麽他就喝什麽

最後来到女方,作为intermediary,他可以决定要让男方就是喝到奶茶,还是混她的口水进去,或是把吸到嘴里的奶茶只分一半给男方,当然也有可能把刚刚吃饭的渣渣给(等等有点恶心了)

接着上图
flow
现在看这张图,两个flow就是吸管,producer的圆形就是饮料杯口,intermediary的方形比喻成女方的嘴巴,consumer的倒D型,当成男方的嘴巴,还有饮料不要吐回去,太恶心了,看到这里,你已经有基本概念了

接着来认真介绍,Flow可分为三个部分,producer、intermediary、consumer,而更重要的是,作为数据流,他可以按顺序发出多个值,他是透过异步处理的数据序列,ex: Flow<dataType>

给个范例,换一个例子

viewModelScope.launch {
    (0..5).asFlow()//producer,奶茶
        .map{//intermediary,女方
            it * 2
        }
        .collect {//consumer,男方
            Timber.d(it.toString())
        }
}

要注意flow是cold stream,意即使用到collect时才会被执行

补充一个重要的点,使用 flow 构建器时,提供方不能提供来自不同 CoroutineContext 的 emit 值。因此,请勿通过创建新协程或使用 withContext 代码块,在不同 CoroutineContext 中调用 emit。在这些情况下,您可使用其他数据流构建器,例如 callbackFlow ----------by.官方文件

疑等等,前面讲过在viewModel用viewModelScope开启coroutine,再调用repository的suspend,透过withContext去切换会更好维护,现在你告诉我flow不能这样做
meme

对,就是这样,第一次看到时有些许混淆视正常的,但请先记住基本原则,flow不仅有自己的操作符可以用来控制diapatcher,他也不需要suspend修式符,最重要的是emit被限制在只有flow builder可以用,所以上面讲的问题,其实都被处理好了

之後会再写一篇范例,留个地方放连结,到时转这边看就会懂了

那实际执行上,到底有甚麽不同

这边都用kotlinConf 2019的范例,因为他们连图都做好了,我可以直接讲

执行

这是一个最基本的flow,实际上的执行是先建立Flow,接着每emit一次,都能立刻拿到一个值

intermediary

记得我们上面的flow流程图吗?他的中间层可以对资料进行操作

以list来说,必须一次处理完整个list,再把整个处理好的list交出去
但flow就不同,她是分别对每个值做处理,所以A处理好,就先给A',B处理好,就先给B'

那现在,我们来更深入了解一下flow的执行,在producer是由上而下执行的,delay只会trigger一次,而在consumer,则是每收到一个值,就会执行block里面的函数一次,所以她收到两个值,就delay两次

恩这很flow

why call cold


以这两个例子来看,list的function会立刻执行,但flow的function却不会执行,除非你呼叫collect() 或是toList()(男方说我要喝奶茶,或我要喝淡奶茶),producer和intermediary才会运作

kotlinConf 2019
flow
flow

用了withContext会怎样

然而,长时间运行的消耗 CPU 的代码也许需要在 Dispatchers.Default 上下文中执行,并且更新 UI 的代码也许需要在 Dispatchers.Main 中执行。通常,withContext 用于在 Kotlin 协程中改变代码的上下文,但是 flow {...} 构建器中的代码必须遵循上下文保存属性,并且不允许从其他上下文中发射(emit)。

如果用了就会~

Exception in thread "main" java.lang.IllegalStateException: Flow invariant is violated:
        Flow was collected in [CoroutineId(1), "coroutine#1":BlockingCoroutine{Active}@5511c7f8, BlockingEventLoop@2eac3323],
        but emission happened in [CoroutineId(1), "coroutine#1":DispatchedCoroutine{Active}@2dae0000, Dispatchers.Default].
        Please refer to 'flow' documentation or use 'flowOn' instead
    at ...

对,他会报错,这种情况要怎麽半?可以用flow on,我明天连我觉得重要的flow操作符一起讲

handle Backpressure

熟悉react stream的开发者,会注意到flow她并没有显式的back pressure处理机制,难道flow没有这个问题?
back pressure - 数据流消费者consumer跟不上数据流生产者producer的速度,所以发一个讯号要求减缓

详细介绍看连结
react stream and flow
node.js backpressure

其实问题还是有,只是kotlin coroutine的开发者,用更优雅的方式解决了,还记得suspend 的功用吧,suspend和resume,当consumer工作量太多,造成back pressure时,可以先把producer挂起,之後再resume

jetbrain - Simple design of Kotlin Flow

连结

flow操作符
pagin3 with flow
mvvm with flow

必看

jetbrain


影片
影片
video

选看

jetBrain Project Lead写的文章


<<:  JavaScript学习日记 : Day20 - call、apply、bind

>>:  [ 卡卡 DAY 17 ] - React Native 用 Animated 来做简单骨架屏

Day27Java StringⅡ

接续昨天,来介绍第四种、第五种以及第六种方法! 4.代替Java String replace():...

找LeetCode上简单的题目来撑过30天啦(DAY6)

今天尝试了前几天被我放弃的题目,过一两天後果然比较有感觉,花了一点时间还是完成了呢,心情不错 题号:...

[Day 30] 阿嬷成为网页前端工程师的下一步

阿嬷成为网页前端工程师的下一步 尽管篇幅长短不一,我们在这 30 天当中讨论了怎麽写 HTML 标签...

【Day30】 晋升成铁人龙猫之总结

哈罗~ 今天是铁人赛的最後一天, 来抢个团队中第一发文的位子XD 之前每几日来个小结, 最後一天就来...

鬼故事 - 印表机最终还是挂了

鬼故事 - 印表机最终还是挂了 https://twitter.com/System32Comics...