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
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()}") } } } }