Kotlin Flow
What is a Flow in Kotlin?
It’s a Kotlin language feature that serves as a reactive programming framwork.
Flow 是 Google 官方提供的一套用于 kotlin 协程的响应式编程模型。Flow 数据流类似于生产者与消费者模式,不过可以在它们之间进行一些链式处理。
class MainViewModel : ViewModel() {
val countDownFlow = flow<Int> {
val startingValue = 10
var currentValue = startingValue
emit(currentValue)
while (currentValue > 0) {
delay(1000L)
currentValue--
emit(currentValue)
}
}
init {
collectFlow()
}
private fun collectFlow() {
viewModelScope.launch {
countDownFlow
.collect { time ->
println("The current time is $time")
}
}
}
}
为什么说 Flow 是冷流呢?
在 countDownFlow 还没有被 collect 时,它是不会去 emit 数据的,只有当它被消费的时候,它才会 emit 生产数据。
Flow 常见的操作符
filter:过滤数据
map:可将数据映射成其他值
private fun commonOperator() {
viewModelScope.launch {
countDownFlow
.filter {
it % 2 == 0
}
.map {
it * it
}
.collect {
println("The value is $it")
}
}
}
onEach:每次 emit 数据后,在收集到数据之前执行
private fun collectFlow() {
viewModelScope.launch {
countDownFlow
.onEach {
println("the other each is $it")
}
.collect { time ->
println("The current time is $time")
} // The collect only confirms the flow finished.
}
}
private fun collectFlow() {
countDownFlow
.onEach {
println("The value is $it")
}
.launchIn(ViewModelScope)
// They are equivalent.
countDownFlow
.collect {
println("The value is $it")
}
}
Terminal Operator
Why call them terminal operator?
Because they terminate the flow so these basically take the whole results of a flow all emissions together and then do something with these.
count:返回所有满足条件的数据个数
private fun countOperator() {
viewModelScope.launch {
val count = countDownFlow
.count {
it % 2 == 0
}
println("The count is $count")
}
}
reduce:有两个参数(accumulator -> 上次所 collect 的数据,value -> 当前 collect 的数据)
private fun reduceOperator() {
viewModelScope.launch {
val reduceResult = countDownFlow
.reduce { accumulator, value ->
accumulator + value // 10 10+9 10+9+8 ...
}
println("The count is $reduceResult") // 10+9+...+1+0
}
}
fold:类似于 reduce,不同之处是多了一个初始值 initValue
private fun foldOperator() {
viewModelScope.launch {
val reduceResult = countDownFlow
.fold(100) { accumulator, value ->
accumulator + value
}
println("The count is $reduceResult") // 100+10+9+...+1+0
}
}
Flatten flows(If we have two flows then we can combine these to have the results coming in a single flow)
flatMapConcat:第一个 flow 必须等待第二个 flow 发送完数据之后,才会继续 emit 发送数据
private fun flattenFlows() {
val flow1 = flow {
emit(1)
delay(3000L)
emit(2)
}
viewModelScope.launch {
flow1.flatMapConcat { value ->
flow {
emit(value + 3)
delay(1000L)
emit(value + 4)
}
}.collect {
Log.d(TAG, "The value is $it")
}
}
}
flatMapMerge:如果第二个 flow 还没有执行完第一个 flow 并不会等待,而是继续 emit 发送数据
private fun flatMapMerge() {
val flow1 = flow {
emit(1)
emit(3)
}
viewModelScope.launch {
flow1.flatMapMerge { value ->
flow {
emit(value + 3)
delay(1000L)
emit(value + 4)
}
}.collect {
Log.d(TAG, "The value is $it")
}
}
}
buffer:flow emit 数据不会等待 collect 结束后才继续 emit,而是持续 emit 数据,若消费者没来得及消费,那么就将产品放入仓库存储,当消费者需要消费时就直接从仓库获取。
private fun restaurantBufferDemo() {
val flow = flow {
delay(500L)
emit("Appetizer")
delay(800L)
emit("Main dish")
delay(200L)
emit("Dessert")
}
viewModelScope.launch {
flow.onEach {
println("FLOW: $it is delivered")
}.buffer().collect {
println("FLOW: Now eating $it")
delay(3000L)
println("FLOW: Finished eating $it")
}
}
}
conflate:与 buffer 不同,仓库中只会存在最新的产品(emit 的数据),旧的产品(emit 的数据)直接丢弃。
private fun restaurantConflateDemo() {
val flow = flow {
delay(500L)
emit("Appetizer")
delay(800L)
emit("Main dish")
delay(200L)
emit("Dessert")
}
viewModelScope.launch {
flow.onEach {
println("FLOW: $it is delivered")
}.conflate().collect {
println("FLOW: Now eating $it")
delay(3000L)
println("FLOW: Finished eating $it")
}
}
}
collectLatest:生产者持续生产产品,消费者只会消费最新的产品,当又有新的产品时,消费者直接丢弃当前产品。
private fun collectLatestDemo() {
val flow = flow {
delay(500L)
emit("Appetizer")
delay(800L)
emit("Main dish")
delay(200L)
emit("Dessert")
}
viewModelScope.launch {
flow.onEach {
println("FLOW: $it is delivered")
}.collectLatest {
println("FLOW: Now eating $it")
delay(3000L)
println("FLOW: Finished eating $it")
}
}
}