Kotlin Flow简单使用

什么是流?

从流的方向来观察,我们称原始数据为上流,对数据进行一系列处理后,最终的数据为下流。
从流的属性来观察,我们认为生产者在上流生产数据,消费者在下流消费数据。

为什么引进Flow?

Flow 是 Kotlin 官方基于协程构建的用于响应式编程的API。响应式编程简单来说就是使用异步数据流进行编程 。协程中,使用挂起函数仅可以异步返回单个值,而 Flow 则可以异步返回多个值,并补全kotlin语言中响应式编程的空白。

Flow常见的操作
  • 生产者消费者例子
    suspend fun collect() {
        flow {
            //发射数据
            emit(5)
        }.collect {
            //消费者
            Log.i("minfo", "value=$it")
        }
    }

通过flow函数构造一个flow对象,然后通过调用flow.collect收集数据。
flow函数的闭包为生产者的生产逻辑,collect函数的闭包为消费者的消费逻辑。

流的三要素:原始数据、对数据的操作、最终数据,对应到Flow上也是一样的。
flow的闭包里我们看做是原始数据,而filter、map、catch等看做是对数据的操作,collect闭包里看做是最终的数据。

  • 寻找1~1000内大于500,并且是7的倍数的数
    suspend fun findNum() {
        var flow = flow {
            for (i in 1..1000) {
                emit(i)
            }
        }.filter {
            it > 500 && it % 7 == 0
        }

        flow.collect {
            Log.i("minfo", "num=$it")
        }
    }
用Flow做倒计时功能:
    var count = MutableLiveData(60)

    fun countDown() {
        viewModelScope.launch {
            flow {
                (60 downTo 0).forEach {
                    delay(1000)
                    emit(it)
                }
            }.flowOn(Dispatchers.Default)
                .onStart {
                    // 倒计时开始
                }
                .onCompletion {
                    // 倒计时结束
                }
                .collect {
                    // 倒计时进行中
                    count.value = count.value?.minus(1)
                }
        }
    }
  • 实现一个计时器的效果,每秒钟更新一次时间
    简单布局:


    

    

点击事件触发:

class MainActivity : AppCompatActivity() {
    private lateinit var tvTime: TextView
    private lateinit var btnStart: Button

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        tvTime = findViewById(R.id.tv_time)
        btnStart = findViewById(R.id.btn_start)
        btnStart.setOnClickListener {
             timeFlow()
        }
    }

    suspend fun timeFlow() {
    fun timeFlow() {
        var timeFlow = flow {
            var time = 0
            while (true) {
                emit(time)
                kotlinx.coroutines.delay(1000)  //使用挂起函数delay
                time++
            }
        }
    }
}

现在的代码下,点击start,会不会就开始发送数据了,在这种场景下不会。因为使用flow构建函数构建出的Flow是属于Cold Flow,也叫做冷流。所谓冷流就是在没有任何接受端的情况下,Flow是不会工作的。只有在有接受端的情况下,Flow闭包中的代码就会自动开始执行。

修改代码让例子生效:

class MainActivity : AppCompatActivity() {
    private lateinit var tvTime: TextView
    private lateinit var btnStart: Button

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        tvTime = findViewById(R.id.tv_time)
        btnStart = findViewById(R.id.btn_start)
        btnStart.setOnClickListener {
            GlobalScope.launch(Dispatchers.Main) {
                timeFlow()
            }
        }
    }

    private suspend fun timeFlow() {
        flow {
            var time = 0
            while (true) {
                emit(time)
                kotlinx.coroutines.delay(1000)  //使用挂起函数delay
                time++
            }
        }.collect {
            tvTime.text = "$it"
        }
    }
}

使用collect函数进行数据接收,由于Flow的collect函数是一个挂起函数,因此必须在协程作用域或其他挂起函数中才能调用。

冷流和热流的区别:

冷流 热流
不消费,不生产,多次消费,多次生产,只有1个观察者 f有没有消费者都会生产数据
感知上游流的异常和启动
val sharedFlow = flow { 
    // 向下游发射100
    emit(100)
    // 向下游发射200
    emit(200)
}.catch {
    // 发生异常,发射-1通知下游
    emit(-1)
}.onStart {
    // 开始启动,向下游发射1
    emit(1)
}
冷流转换热流的使用与原理

在协程中,通过调用操作符shareIn与stateIn,可以将一个冷流转换成一个热流,这两个方法的区别如下:

shareIn:将一个冷流转换成一个标准的热流——SharedFlow类型的对象。
stateIn:将一个冷流转换成一个单数据更新的热流——StateFlow类型的对象。

参考:
https://www.jianshu.com/p/a72c4637c546

你可能感兴趣的:(Kotlin Flow简单使用)