Kotlin - Flow 冷流、热流

Kotlin - Flow 冷流、热流

‌Kotlin Flow‌是Kotlin协程的一部分,旨在简化异步编程,提供了一种声明式的方式来处理数据流。
Flow允许以非阻塞的方式处理一系列的值或事件,特别适合于处理大量数据或涉及IO操作的情况。

Kotlin 协程中使用挂起函数可以实现非阻塞地执行任务并将结果返回,但是只能返回单个计算结果。但如果希望有多个计算结果返回,则可以使用Flow。

Flow的三大核心:
1、生产者(厨房):把做好的菜(数据)一盘一盘放到传送带上(emit)。
2、中间操作(传送带上的加工站):给菜加调料(map)、过滤坏掉的菜(filter)。
3、消费者(你):从传送带末尾取菜吃(collect)。

基本概念和特性
1、‌异步流‌:Flow允许以非阻塞的方式处理一系列的值或事件,这使得在处理大量数据或涉及IO操作时能够更加高效‌。
2、‌冷流(Cold Flow)和热流(Hot Flow)‌:
   冷流在订阅后才开始发射数据,而热流在创建后立即开始发射数据,不管是否有订阅者‌
3、‌声明式API‌:Flow提供了一套简洁清晰的操作符,如map、filter、reduce等,允许以声明式的方式对流进行处理‌
4、‌协程集成‌:Flow构建在协程之上,可以与协程一起使用,利用协程的轻量级、顺序性等优势‌
5、‌背压支持‌:Flow提供了背压支持,可以通过各种操作符来控制数据的生产和消费速率,防止生产者速度过快导致消费者无法跟上‌

使用方法
‌创建冷流Flow‌:可以通过flow关键字创建一个Flow对象。例如:
1、flow()        : flow{emit(2)}
2、flowOf() : flowOf(2,3,)
3、asFlow() : listOf(2, 3,).asFlow()

Flow API 具体可以分为三类:
普通的Flow API:最早诞生,属于数据流
StateFlow:状态流,是特殊的 
SharedFlow:事件流

冷流(Cold Flow),指的是开始收集(collect())数据的时候才开始生产数据(点播电影,点播时才开始播放,不同观众各自从头看)
    在数据被使用方订阅后,即调用 collect 方法之后,提供方才开始执行发送数据流的代码,通常是调用 emit 方法。即不消费,不生产,多次消费才会多次生产。使用方和提供方是一对一的关系。
    flow { ... }、asFlow()

热流(Hot Flow),指的是没收集数据的时候也可以生产数据(看直播,中途加入只能看到当前的内容)
    无论有无使用方,提供方都可以执行发送数据流的操作,提供方和使用方是一对多的关系。热流就是不管有无消费,都可生产。
    StateFlow:状态管理,需要持久化状态、实时同步最新值。
        始终持有当前状态值(必须初始值)
        只向收集者发射最新值和后续更新
        value和emit区别,作用都是发射数据
        value: 任何地方都可以调用
        emit:只能在协程中调用,因为emit是挂起函数suspend
        
    SharedFlow:需要灵活的事件分发、历史事件重放或复杂缓存策略。
        没有初始值,也不一定有当前值。
        可以配置缓存历史值(通过 replay 参数)
        可以配置缓存策略(通过 extraBufferCapacity 参数)
        支持多个订阅者共享同一流
    
使用 flowOn 切线程:将耗时操作切到后台线程,避免卡主线程。
缓冲处理buffer():让生产者和消费者并行工作,减少等待时间。
合并快速发射的数据conflate():只处理最新数据,跳过中间值(适合实时性要求高的场景)。
背压处理(Backpressure):生产者发数据太快,消费者处理不过来,导致数据积压(像堵车)。

例子:

/**
 * Author : wn
 * Email : [email protected]
 * Date : 2025/3/28 17:10
 * Description : 冷流、热流
 */
class TestFlowFragment : Fragment() , View.OnClickListener{

    private val mainScope = MainScope()

    private lateinit var binding : FlowMainBinding

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = FlowMainBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        binding.flowBtn1.setOnClickListener(this)
        binding.flowBtn2.setOnClickListener(this)
        binding.flowBtn3.setOnClickListener(this)
        binding.flowBtn4.setOnClickListener(this)
        binding.flowBtn5.setOnClickListener(this)
        binding.flowBtn6.setOnClickListener(this)

    }


    override fun onClick(v: View?) {
        v?:return

        when(v.id){
            R.id.flow_btn1 -> testFlow()
            R.id.flow_btn2 -> testSharedFlow()
            R.id.flow_btn3 -> testStateFlow()
            R.id.flow_btn4 -> testFlowOn()
            R.id.flow_btn5 -> testFlowBuffer()
            R.id.flow_btn6 -> testFlowConflate()
        }

    }

    //测试冷流:只有调用 collect() 时,才开始生产数据
    private fun testFlow(){
        //collect是挂起函数suspend,必须在协程中处理
        CoroutineScope(Dispatchers.Main).launch {
            getDataFlow()
                .filter { it % 2 == 0 }
                .map { "结果:${it}" }
                .collect{result ->
                    LogUtils.i("AAA", "${result}")
                }

            Thread.sleep(5000)

            getDataFlow()
                .filter { it % 2 != 0 }
                .map { "延迟结果:${it}" }
                .collect{result ->
                    LogUtils.e("AAA", "${result}")
                }
        }
    }

    private fun getDataFlow() : Flow = flow{
        val list = mutableListOf(1,4,6,3,8,9,2, 11, 22, 23, 7)
        list.forEach {
            delay(1000)
            emit(it)
        }
    }

    //测试热流 - 状态管理
    private fun testStateFlow(){
        val temperatureStateFlow = MutableStateFlow(25)

        //发射多个值, collect只接收到最新的29
        temperatureStateFlow.value = 28
        temperatureStateFlow.value = 29

        //value和emit都是发射数据,但emit必须在协程中执行
        /*CoroutineScope(Dispatchers.IO).launch {
            temperatureStateFlow.emit(26)
        }*/

        CoroutineScope(Dispatchers.Main).launch {
            temperatureStateFlow.collect{
                LogUtils.w("AAA", "StateFlow接收数据:${it}")
            }
        }

    }

    //测试热流:不管是否收集数据,都会发射数据
    private fun testSharedFlow(){
        //创建热流
        val sharedFlow = MutableSharedFlow()

        //创建协程持续发射数据
        CoroutineScope(Dispatchers.IO).launch {
            repeat(3){
                delay(1000)
                LogUtils.w("AAA", "发射数据:${it}")
                sharedFlow.emit(it) //发射0,1,2, 间隔1秒
            }
        }

        //第一个订阅者
        CoroutineScope(Dispatchers.Main).launch {
            delay(1500)
            sharedFlow.collect{
                LogUtils.i("AAA", "第1个接收:${it}") // 只能接收到1,2 。 接收不了0
            }
        }

        //第二个订阅者
        CoroutineScope(Dispatchers.Main).launch {
            delay(4000)
            sharedFlow.collect{
                LogUtils.e("AAA", "第2个接收:${it}") //接收不了任何数据, 因为暂停4秒,发射数据已结束
            }
        }

        //第三个订阅者
        CoroutineScope(Dispatchers.Main).launch {
            sharedFlow.collect{
                LogUtils.e("AAA", "第3个接收:${it}")
            }
        }

    }



    //冷流-合并快速发射数据(丢弃中间值,只保留最新数据,实时数据(如UI刷新,只需最新状态))
    private fun testFlowConflate(){
        mainScope.launch {
            flow {
                repeat(100){
                    emit(it)
                }
            }.conflate()
                .collect{
                    LogUtils.i("AAA", "快速发射:${it}")
                    delay(100)
                }
        }
    }

    //冷流-缓存buffer
    private fun testFlowBuffer(){
        mainScope.launch {
            flow {
                repeat(100){
                    emit(it)
                }
            }.buffer(50)
                .collect{
                    LogUtils.i("AAA", "冷流buffer ${it}")
                    delay(1000)
                }
        }

    }

    //冷流-切换线程
    private fun testFlowOn(){
        val flow1 = flow {
            LogUtils.i("AAA", "1当前线程:${Thread.currentThread()}")
            emit(1)
        }

        //切换到IO线程执行
        val flow2 = flow {
            LogUtils.e("AAA", "2当前线程:${Thread.currentThread()}")
            emit(2)
        }.flowOn(Dispatchers.IO)

        lifecycleScope.launch {
            flow1.collect{
                LogUtils.i("AAA", "1当前: ${it} , 线程:${Thread.currentThread()}")
            }

            flow2.collect{
                LogUtils.e("AAA", "2当前: ${it} , 线程:${Thread.currentThread()}")
            }
        }

    }

}

你可能感兴趣的:(Kotlin例子,kotlin,开发语言,android)