文章目录
概述
数据流以协程为基础构建,可提供多个值。从概念上来讲,数据流是可通过异步方式进行计算处理的一组数据序列。所发出值的类型必须相同。例如,Flow 是发出整数值的数据流。
在协程中,与仅返回单个值的挂起函数相反,数据流可按顺序发出多个值。
数据流包含三个实体:
- 提供方会生成添加到数据流中的数据。得益于协程,数据流还可以异步生成数据。
- (可选)中介可以修改发送到数据流的值,或修正数据流本身。
- 使用方则使用数据流中的值。

1.使用
1.1 创建数据流
创建数据流有两种常用方式: 第一就是试用数据流构建函数, 第二就是利用扩展方法将原有类型转换成Flow
使用数据构建函数
这里比较常用的函数是 flow() 和 flowOf()
val f = flow {
emit(1)
}
val f2 = flowOf("abc")
val f3 = flowOf("A", "B", "C")
其实 flowof() 是简化版的 flow() 函数,内部帮我们调用了 emit()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JEWWqcWq-1670402003040)(/Users/apple/Downloads/job_files/flow/Xnip2022-11-20_00-25-28.jpg )]
注意:
使用 flow 构建器时,提供方不能提供来自不同 CoroutineContext 的 emit 值。因此,请勿通过创建新协程或使用 withContext 代码块,在不同 CoroutineContext 中调用 emit 可以使用 flowOn 或者 callbackFlow。
使用扩展方法
扩展方法是 kotlin 标准库中提供的 asFlow() . 目前基本上覆盖了基本数据类型、lambda、及挂起函数等
下面看一下具体如何使用:
普通lambda 表达式
fun testAsFlow1(blacking: () -> String) {
val flow = blacking.asFlow()
}
挂起函数
suspend fun testAsFlow2(blacking: suspend () -> String) {
val flow = blacking.asFlow()
}
或者以下方式:
suspend fun remoteCall(): String = withContext(Dispatchers.IO) {
return@withContext "ABC"
}
fun remoteCallFlow(): Flow<String> = ::remoteCall.asFlow()
基本数据类型
kotlin 标准库中提供了基本数据类型相关扩展方法,其中 Map类型不能直接使用asFlow() 需要转换成其支持的类型,再使用 asFlow().
fun testAsFlow3() {
// Range 类型
val flow1 = IntRange(1, 100).asFlow()
val flow2 = LongRange(1L, 1000L).asFlow()
// Array 类型
val flow3 = intArrayOf(1, 2, 3).asFlow()
val flow4 = longArrayOf(1L, 2L, 3L).asFlow()
val flow5 = arrayOf("A", "B", "C").asFlow()
// Sequence 类型
val flow6 = sequenceOf("X", "Y", "Z").asFlow()
// Iterable 类型
val flow7 = listOf(1, 2, 3).asFlow()
val flow8 = setOf("E", "G", "C").asFlow()
// Iterator 类型
val flow9 = iterator<Int> {}.asFlow()
// Map 类型
val map = hashMapOf(Pair("A", 1), Pair("B", 2))
val flow10 = map.asIterable().asFlow()
val flow11 = map.asSequence().asFlow()
val flow12 = map.iterator().asFlow()
}
1.2 修改数据流
可以利用中间运算符在不使用值的情况下修改数据流。中间运算符可以接连应用,形成链式运算,在数据项被发送到数据流时延迟执行。
其中操作符还分为三大类:
- 创建操作符
- 中间操作符
- 末端操作符

关于Flow 操作符可以查看这篇文章 Flow 操作符大全
1.3 收集数据
使用 collect 进行收集数据
val f = emptyFlow<Int>()
.collect {
// ignore
}
2. 冷流 与 热流
所谓 冷流 和 热流 是根据数据收集时是否出发提供方代码而定义的。
冷流: 即 收集数据时才触发提供方代码,即为冷流。我们使用 flow 或 flowOf 创建的 Flow 都是冷流。
热流: 数据收集时数据已经存在,不需要触发提供方代码,这样的流即为 热流。StateFlow 、SharedFlow 这两个都是热流。
StateFlow
StateFlow 是热数据流:从数据流收集数据不会触发任何提供方代码。StateFlow 始终处于活跃状态并存于内存中,而且只有在垃圾回收根中未涉及对它的其他引用时,它才符合垃圾回收条件。
StateFlow 是一个状态容器式可观察数据流,可以向其收集器发出当前状态更新和新状态更新。
在 Android 中,StateFlow 非常适合需要让可变状态保持可观察的类。
// StateFlow
private val _uiStateMutableStateFlow: MutableStateFlow<UIState> = MutableStateFlow(UIState.INITIAL)
val uiStateStateFlow: StateFlow<UIState> = _uiStateMutableStateFlow
StateFlow、Flow 和 LiveData
StateFlow 和 LiveData 具有相似之处。两者都是可观察的数据容器类,并且在应用架构中使用时,两者都遵循相似模式。
StateFlow 和 LiveData 的行为确实有所不同:
StateFlow需要将初始状态传递给构造函数,而LiveData不需要。- 当
View进入STOPPED状态时,LiveData.observe()会自动取消注册使用方,而从StateFlow或任何其他数据流收集数据的操作并不会自动停止。
SharedFlow
SharedFlow 是 StateFlow 的可配置性极高的泛化数据流, StateFlow 本身继承于 SharedFlow。
private val _uiStateSharedFlow: MutableSharedFlow<UIState> = MutableSharedFlow(1, 3, BufferOverflow.DROP_OLDEST)
val uiStateSharedFlow: SharedFlow<UIState> = _uiStateSharedFlow
创建 SharedFlow 需要三个参数:
replay: 重新给订阅者发送之前值的数量,默认是 0extraBufferCapacity: 缓冲区数量, 默认为 0 。不包含replay配置数量onBufferOverflow: 缓冲区数据存满发送时策略,BufferOverflow类型枚举,默认值BufferOverflow.SUSPEND。
BufferOverflow 有三种策略:
SUSPEND: 当缓冲区已满时,正在发送或发出值的上游被挂起。DROP_OLDEST: 缓冲区满时删除缓冲区中最旧的值,将新值添加到缓冲区,不进行挂起。DROP_LATEST:缓冲区满时删除立即添加到缓冲区的最新值(以便缓冲区内容保持不变),不进行挂起。
冷流和热流转换
shareIn
将普通flow 转化为 SharedFlow , 其有三个参数
- scope:
CoroutineScope开始共享的协程范围 - started:
SharingStarted控制何时开始和停止共享的策略 - replay: Int = 0 发给 新的订阅者 的旧值数量
其中 started 有一些可选项:
Eagerly: 共享立即开始,永不停止Lazily: 当第一个订阅者出现时, 永不停止WhileSubscribed: 在第一个订阅者出现时开始共享,在最后一个订阅者消失时立即停止(默认情况下),永久保留重播缓存(默认情况下)
WhileSubscribed 具有以下可选参数:
stopTimeoutMillis— 配置最后一个订阅者消失到协程停止共享之间的延迟(以毫秒为单位)。 默认为零(立即停止)。replayExpirationMillis- 共享的协程从停止到重新激活,这期间缓存的时效
val shareIn = flowOf(1, 2, 3)
.shareIn(viewModelScope, SharingStarted.Eagerly, 10)
stateIn
将普通flow 转化为 StateFlow 。 其有三个参数:
- scope - 开始共享的协程范围
- started - 控制何时开始和停止共享的策略
- initialValue - 状态流的初始值
val stateIn = flowOf(1)
.stateIn(viewModelScope, SharingStarted.Lazily, 1)
3. Android 中使用Flow
由于 Flow 流的发送和接收只是在 协程生命周期中,而没有和 Android 的一些生命周期组件,如 Activity/Fragment 绑定,所以 Android 推出了与 Lifecycle 生命周期组件相关的库和扩展方法,以方便我们在合理的生命周期内收集数据。
repeatOnLifecycle
repeatOnLifecycle 是 Lifecycle 生命周期组件,扩展方法。用于在合适的生命周期期间接收 flow 数据 。
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
uiStateFlow.collect { uiState ->
updateUi(uiState)
}
}
}
源码分析
public suspend fun Lifecycle.repeatOnLifecycle(
state: Lifecycle.State,
block: suspend CoroutineScope.() -> Unit
) {
// 如果 Lifecycle 组件处于 INITIALIZED 则直接抛出错误
require(state !== Lifecycle.State.INITIALIZED) {
"repeatOnLifecycle cannot start work with the INITIALIZED lifecycle state."
}
// 如果 Lifecycle 组件处于 DESTROYED 停止
if (currentState === Lifecycle.State.DESTROYED) {
return
}
// 开启一个协程,并切换到主线程中,后续所有代码均运行在此线程中
coroutineScope {
withContext(Dispatchers.Main.immediate) {
// 再次检查当前生命周期状态,如果是 DESTROYED 则取消
if (currentState === Lifecycle.State.DESTROYED) return@withContext
// 协程
var launchedJob: Job? = null
// 生命周期观察回调
var observer: LifecycleEventObserver? = null
try {
// 创建一个协程,这个协程除非生命周期结束或者被取消,否则一直处于被挂起状态
suspendCancellableCoroutine<Unit> { cont ->
// 检查生命周期执行区间
val startWorkEvent = Lifecycle.Event.upTo(state)
val cancelWorkEvent = Lifecycle.Event.downFrom(state)
val mutex = Mutex()
// 创建 Lifecycle 生命周期观察者
observer = LifecycleEventObserver { _, event ->
if (event == startWorkEvent) {
// Launch the repeating work preserving the calling context
launchedJob = this@coroutineScope.launch {
// 重新开启协程执行,lambda 方法块
mutex.withLock {
coroutineScope {
block()
}
}
}
return@LifecycleEventObserver
}
// 取消协程
if (event == cancelWorkEvent) {
launchedJob?.cancel()
launchedJob = null
}
// 关闭外部 协程,内部使用状态机
if (event == Lifecycle.Event.ON_DESTROY) {
cont.resume(Unit)
}
}
this@repeatOnLifecycle.addObserver(observer as LifecycleEventObserver)
}
} finally { // 取消协程 & 移除观察者
launchedJob?.cancel()
observer?.let {
this@repeatOnLifecycle.removeObserver(it)
}
}
}
}
}
以上就是 repeatOnLifecycle 方法的源码,注释比较详细,总的来说,就两点:
- 开启协程,创建
Lifecycle观察者,在符合生命周期期间进行执行外部代码块 - 生命周期结束之后,取消协程、移除观察者等
需要注意的是:
在调用
repeatOnLifecycle()时state不能传入Lifecycle.State.INITIALIZED和Lifecycle.State.DESTROYED
另外 LifecycleOwner 也有同名扩展方法,更加方便使用,其内部也是调用 Lifecycle的扩展方法。
public suspend fun LifecycleOwner.repeatOnLifecycle(
state: Lifecycle.State,
block: suspend CoroutineScope.() -> Unit
): Unit = lifecycle.repeatOnLifecycle(state, block)
flowWithLifecycle
flowWithLifecycle 是 Flow 的扩展方法,为方便Flow 使用,其内部也是通过 Lifecycle 的 repeatOnLifecycle() 实现,但还是有点区别。
使用:
lifecycleScope.launch {
flow.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.filter { }
.map { }
.collect {
// Consume flow emissions
}
}
从使用上看 flowWithLifecycle() 更像是一个操作符,因为他返回的还是 Flow 。还可以继续使用其他操作符,最后再使用 末端操作符来接收值。
public fun <T> Flow<T>.flowWithLifecycle(
lifecycle: Lifecycle,
minActiveState: Lifecycle.State = Lifecycle.State.STARTED
): Flow<T> = callbackFlow {
lifecycle.repeatOnLifecycle(minActiveState) {
this@flowWithLifecycle.collect {
// 将值重新发送到 flow 中
send(it)
}
}
// 关闭 flow
close()
}
flowWithLifecycle 的源码比较简单,内部创建一个 callbackFlow 用于将 ifecycle.repeatOnLifecycle() 收集结果,再发送至 Flow 中,这样下游依然能使用 collect 来收集上游的结果。最后在 协程关闭时将 flow 关闭掉 。
参考
https://developer.android.com/kotlin/flow?hl=zh-cn
https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/
https://kotlinlang.org/docs/flow.html
本文深入探讨了Android中Kotlin的Flow数据流的使用,包括创建、修改和收集数据的方法,以及冷流和热流的概念。重点介绍了StateFlow、SharedFlow的区别,以及如何在Android环境中有效利用Flow,如repeatOnLifecycle和flowWithLifecycle方法。同时,文章通过源码分析帮助读者更好地理解Flow的工作原理。
487

被折叠的 条评论
为什么被折叠?



