Kotlin 协程 (十六) ——— StateFlow 简介

809 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

本文我们介绍热流 StateFlow,前面我们已经介绍过热流和冷流的区别。StateFlow 不管有没有被收集,都会处于活跃状态,并且发射的值都会存在于内存中。

StateFlow 同时具有 LiveData 的特点,它包含一个可观测的值。

一、使用 StateFlow 实现 LiveData

我们先用 StateFlow 来实现类似 LiveData 的功能:

runBlocking {
    println("init stateFlow with 0")
    val stateFlow = MutableStateFlow(0)
    val job = launch {
        stateFlow.collect {
            println("received: $it")
        }
    }
    delay(1000)
    println("set stateFlow.value = 1")
    stateFlow.value = 1
    delay(1000)
    println("set stateFlow.value = 2")
    stateFlow.value = 2
    delay(1000)
    job.cancel()
}

首先,我们以 MutableStateFlow(0) 创建出一个初始值为 0,包含 Int 数据的 StateFlow。然后开启一个协程,使用 collect 函数收集 StateFlow 的值。

然后间隔 1s 修改一次 StateFlow 的值,运行程序,输出如下:

init stateFlow with 0
received: 0
set stateFlow.value = 1
received: 1
set stateFlow.value = 2
received: 2

这样的效果和在 Activity 中使用 LiveData 的效果是一致的:

println("init liveData with 0")
val liveData = MutableLiveData(0)
liveData.observe(this) {
    println("received: $it")
}
lifecycleScope.launchWhenCreated {
    delay(1000)
    println("set liveData.value = 1")
    liveData.value = 1
    delay(1000)
    println("set liveData.value = 2")
    liveData.value = 2
}

运行程序,输出如下:

init liveData with 0
received: 0
set liveData.value = 1
received: 1
set liveData.value = 2
received: 2

需要注意的是,如果值发送得太快,collect 时只会收到最后一个值,这个特性和 LiveData 也是一样的。比如此例中,如果去掉所有 delay,运行结果如下:

init liveData with 0
set liveData.value = 1
set liveData.value = 2
received: 2

在 StateFlow 的例子中,去掉 delay:

runBlocking {
    println("init stateFlow with 0")
    val stateFlow = MutableStateFlow(0)
    val job = launch {
        stateFlow.collect {
            println("received: $it")
        }
    }
    println("set stateFlow.value = 1")
    stateFlow.value = 1
    println("set stateFlow.value = 2")
    stateFlow.value = 2
    delay(1000)
    job.cancel()
}

运行结果如下:

init stateFlow with 0
set stateFlow.value = 1
set stateFlow.value = 2
received: 2

可以看到,只有最后一个值被收集到了。

二、使用 StateFlow 的 Flow 特性

StateFlow 不仅是一个可观测的值,而且是 Flow 的一种,所以具有 Flow 的特性:

runBlocking {
    println("init stateFlow with 0")
    val stateFlow = MutableStateFlow(0)
    val job = launch {
        stateFlow.map {
            "converted $it"
        }.collect {
            println("received: $it, stateFlow.value = ${stateFlow.value}")
        }
    }
    delay(1000)
    println("set stateFlow.value = 1")
    stateFlow.value = 1
    delay(1000)
    job.cancel()
}

我们在 collect 前,利用 Flow 中的 map 操作符将收集到的值进行转换。运行程序,输出如下:

init stateFlow with 0
received: converted 0, stateFlow.value = 0
set stateFlow.value = 1
received: converted 1, stateFlow.value = 1

可以看到,Flow 中的操作符仍然可以在 StateFlow 中使用。同时,我们可以直接使用 stateFlow.value 读取 StateFlow 中的原始值。

三、小结

StateFlow 是一种特殊的 Flow,它是一种状态容器式的可观测数据流。它的使用场景和 LiveData 很类似,可以完全替代 LiveData。但同时兼具 Flow 的特性,可以在下游收集数据前对数据进行转换操作,也可以方便地切换调度器。