一手遮天 Android - kotlin 协程: Flow(异步流,各种操作符的使用 buffer, conflate, collectLatest, drop, take, filter, map, transform, onEach, first, last, single, reduce, zip, combine, flatMapConcat, flatMapMerge 等)

项目地址 https://github.com/webabcd/AndroidDemo
作者 webabcd

一手遮天 Android - kotlin 协程: Flow(异步流,各种操作符的使用 buffer, conflate, collectLatest, drop, take, filter, map, transform, onEach, first, last, single, reduce, zip, combine, flatMapConcat, flatMapMerge 等)

示例如下:

/kotlin/coroutine/Demo7.kt

/**
 * flow - 异步流
 *
 * 本例用于演示
 * 1、优化发送/接收数据(buffer, conflate, collectLatest)
 * 2、数组转换为 flow 以及 flow 的数据处理相关的操作符(drop, take, filter, map, transform, onEach, first, last, single, reduce 等)
 * 3、将两个 flow 组合为一个 flow(zip, combine)
 * 4、flow 内嵌套 flow(flatMapConcat, flatMapMerge)
 */

package com.webabcd.androiddemo.kotlin.coroutine

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import com.webabcd.androiddemo.R
import kotlinx.android.synthetic.main.activity_kotlin_coroutine_demo7.button1
import kotlinx.android.synthetic.main.activity_kotlin_coroutine_demo7.button2
import kotlinx.android.synthetic.main.activity_kotlin_coroutine_demo7.button3
import kotlinx.android.synthetic.main.activity_kotlin_coroutine_demo7.button4
import kotlinx.android.synthetic.main.activity_kotlin_coroutine_demo7.textView1
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import java.text.SimpleDateFormat
import java.util.*

class Demo7 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_kotlin_coroutine_demo7)

        // 优化发送/接收数据(buffer, conflate, collectLatest)
        button1.setOnClickListener {
            sample1()
        }

        // 数组转换为 flow 以及 flow 的数据处理相关的操作符(drop, take, filter, map, transform, onEach, first, last, single, reduce 等)
        button2.setOnClickListener {
            sample2()
        }

        // 将两个 flow 组合为一个 flow(zip, combine)
        button3.setOnClickListener {
            sample3()
        }

        // flow 内嵌套 flow(flatMapConcat, flatMapMerge)
        button4.setOnClickListener {
            sample4()
        }
    }

    fun sample1() {
        val flow = flow {
            for (i in 1..3) {
                delay(200)
                emit(i)
            }
        }

        CoroutineScope(Dispatchers.Default).launch {
            // 常规写法,发送数据会阻塞,直到发送的数据被接收为止;接收数据会阻塞,直到接收到数据为止
            flow
                .collect { value ->
                    delay(500)
                    appendMessage("$value")
                }
            // 每 700 毫秒会收到一条数据
            // 03:07:39.761: 1(DefaultDispatcher-worker-1)
            // 03:07:40.481: 2(DefaultDispatcher-worker-1)
            // 03:07:41.188: 3(DefaultDispatcher-worker-1)


            // 通过 buffer() 设置一个缓冲区,可以指定缓冲区保存数据的最大条数
            flow
                .buffer(64)
                .collect { value ->
                    delay(500)
                    appendMessage("$value")
                }
            // 每 500 毫秒会收到一条数据
            // 03:07:41.937: 1(DefaultDispatcher-worker-2)
            // 03:07:42.445: 2(DefaultDispatcher-worker-2)
            // 03:07:42.958: 3(DefaultDispatcher-worker-2)


            // 通过 conflate() 可以实现,在你来不及接收数据的时候丢弃数据
            flow
                .conflate()
                .collect { value ->
                    delay(500)
                    appendMessage("$value")
                }
            // emit 第 2 条数据时,collect 正在忙其他事情来不及接收,所以就丢弃第 2 条数据了
            // 03:07:43.725: 1(DefaultDispatcher-worker-2)
            // 03:07:44.230: 3(DefaultDispatcher-worker-2)


            // 通过 collectLatest {} 可以实现,在发来的数据你来不及接收的时候,立刻停止你正在忙的工作,马上去从接收数据开始重新走一遍逻辑(相当于重启接收了)
            flow
                .collectLatest { value ->
                    appendMessage("collect: $value")
                    delay(300)
                    appendMessage("done: $value")
                }
            // 在发来的数据你来不及接收的时候,立刻停止你正在忙的工作,马上去从接收数据开始重新走一遍逻辑(相当于重启接收了)
            // 03:07:44.450: collect: 1(DefaultDispatcher-worker-2)
            // 03:07:44.662: collect: 2(DefaultDispatcher-worker-1)
            // 03:07:44.866: collect: 3(DefaultDispatcher-worker-1)
            // 03:07:45.168: done: 3(DefaultDispatcher-worker-1)
        }
    }

    fun myMap(input: Int): String {
        return "map $input"
    }
    fun myFilter(input: Int): Boolean {
        return input > 28
    }
    fun sample2() {
        // asFlow() - 数组转 flow
        val a = (1..100).asFlow()
        // flowOf() - 数组转 flow
        val b = flowOf(1..100)

        CoroutineScope(Dispatchers.Default).launch {
            (1..100)
                .asFlow()
                .drop(10)       // 舍弃前 n 条数据
                .take(20)       // 只获取前 n 条数据
                .filter { value ->    // 只获取符合条件的数据
                    myFilter(value)
                }.map { value ->      // 按照自定义逻辑转换数据
                    myMap(value)
                }.onEach { value ->   // 在数据发给 collect {} 之前调用
                    appendMessage("onEach: $value")
                }.collect { value ->
                    appendMessage("collect: $value")
                }
            // onEach: map 29(DefaultDispatcher-worker-1)
            // collect: map 29(DefaultDispatcher-worker-1)
            // onEach: map 30(DefaultDispatcher-worker-1)
            // collect: map 30(DefaultDispatcher-worker-1)


            (1..2)
                .asFlow()
                .transform { value ->   // 按照自定义逻辑处理数据,你可以转换数据并决定是否发送数据
                    // 发送你换后的数据,不调用 emit() 的话收集器就收不到数据了
                    emit( "transform: $value")
                }.collect { value ->
                    appendMessage(value)
                }
            // transform: 1(DefaultDispatcher-worker-1)
            // transform: 2(DefaultDispatcher-worker-1)


            var result =
                (1..5)
                    .asFlow()           // flow 支持 first(), last(), single() 之类的方法,都比较简单,就不演示了
                    .reduce { a, b ->   // 统计数据,本例演示了如何通过 reduce 实现数据累加的功能
                        appendMessage("reduce: $a, $b")
                        a + b
                    }
            appendMessage("result: $result")
            // reduce: 1, 2(DefaultDispatcher-worker-1)
            // reduce: 3, 3(DefaultDispatcher-worker-1)
            // reduce: 6, 4(DefaultDispatcher-worker-1)
            // reduce: 10, 5(DefaultDispatcher-worker-1)
            // result: 15(DefaultDispatcher-worker-1)
        }
    }

    fun sample3() {
        CoroutineScope(Dispatchers.Default).launch {
            // 这里的 onEach 用于模拟长时任务
            val flow1 = flowOf(1, 2).onEach { delay(100) }
            // 这里的 onEach 用于模拟长时任务
            val flow2 = flowOf("a", "b", "c").onEach { delay(500) }

            // zip() - 用于把两个 flow 组合成一个 flow
            flow1.zip(flow2) { a, b -> // 两个 flow 会互相等待,等两个 flow 都收到了数据时,就会走到这里,有一个 flow 退出了组合体就会退出
                "$a -> $b"
            }.collect { appendMessage(it) }
            // 03:24:48.873: 1 -> a(DefaultDispatcher-worker-2)
            // 03:24:49.351: 2 -> b(DefaultDispatcher-worker-2)


            // combine() - 用于把两个 flow 组合成一个 flow
            flow1.combine(flow2) { a, b -> // 两个 flow 分别接收数据,接收慢的 flow 收到数据时会走到这里(接收快的那个此处会保存最新接收的值),两个 flow 都退出了组合体就会退出
                "$a -> $b"
            }.collect { appendMessage(it) }
            // 03:24:49.915: 2 -> a(DefaultDispatcher-worker-1)
            // 03:24:50.421: 2 -> b(DefaultDispatcher-worker-1)
            // 03:24:50.922: 2 -> c(DefaultDispatcher-worker-1)

        }
    }

    fun myFlow(i: Int): Flow<String> = flow {
        delay(300)
        emit("emit: $i")
    }
    fun sample4() {
        CoroutineScope(Dispatchers.Default).launch {
            (1..3)
                .asFlow()
                .onEach { delay(500) } // 这里的 onEach 用于模拟长时任务
                .flatMapConcat { // 在 flow 内嵌套另一个 flow,主 flow 和 嵌套 flow 是顺序执行的
                    myFlow(it)
                }
                .collect { value ->
                    appendMessage("$value")
                }
            // 03:30:18.268: emit: 1(DefaultDispatcher-worker-1)
            // 03:30:19.078: emit: 2(DefaultDispatcher-worker-1)
            // 03:30:19.886: emit: 3(DefaultDispatcher-worker-1)


            (1..3)
                .asFlow()
                .onEach { delay(500) } // 这里的 onEach 用于模拟长时任务
                .flatMapMerge(64) { // 在 flow 内嵌套另一个 flow,主 flow 和 嵌套 flow 是并行执行的,可以指定最大并发数
                    myFlow(it)
                }
                .collect { value ->
                    appendMessage("$value")
                }
            // 03:30:20.784: emit: 1(DefaultDispatcher-worker-2)
            // 03:30:21.278: emit: 2(DefaultDispatcher-worker-1)
            // 03:30:21.778: emit: 3(DefaultDispatcher-worker-2)
        }
    }



    fun appendMessage(message: String) {
        val dateFormat = SimpleDateFormat("HH:mm:ss.SSS", Locale.ENGLISH)
        val time = dateFormat.format(Date());
        val threadName = Thread.currentThread().name

        CoroutineScope(Dispatchers.Main).launch{
            val log = "$time: $message($threadName)"
            textView1.append(log);
            textView1.append("\n");

            Log.d("coroutine", log)
        }
    }
}

/layout/activity_kotlin_coroutine_demo7.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="优化发送/接收数据(buffer, conflate, collectLatest)"/>

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="数组转换为 flow 以及 flow 的数据处理相关的操作符(drop, take, filter, map, transform, onEach, first, last, single, reduce 等)"/>

    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="将两个 flow 组合为一个 flow(zip, combine)"/>

    <Button
        android:id="@+id/button4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="flow 内嵌套 flow(flatMapConcat, flatMapMerge)"/>

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

项目地址 https://github.com/webabcd/AndroidDemo
作者 webabcd

posted @ 2022-07-13 21:09  webabcd  阅读(1805)  评论(0编辑  收藏  举报