flow是kotlin提供的异步数据流api,可以按照顺序依次发送多个值,写法和RxJava很像。
flow分成两种类型,冷流和热流:
- 冷流:通过
flow方法创建的流。 - 热流:SharedFlow和StateFlow
冷流
使用方法
kotlin
val flow = flow {
Log.e("TAG", "flow: start")
emit(1)
delay(100)
emit(2)
delay(100)
emit(3)
}
flow.collect {
Log.e("TAG", "collect: $it")
}

使用flow方法创建一个SafeFlow的对象,然后调用他的collect方法,就会依次收到emit发送的数据。
原理
kotlin
/**
* 在上面的例子中
* block = {
* Log.e("TAG", "flow: start")
* emit(1)
* delay(100)
* emit(2)
* delay(100)
* emit(3)
* }
*/
public fun <T> flow(@BuilderInference block: suspend FlowCollector<T>.() -> Unit): Flow<T> = SafeFlow(block)
private class SafeFlow<T>(private val block: suspend FlowCollector<T>.() -> Unit) : AbstractFlow<T>() {
override suspend fun collectSafely(collector: FlowCollector<T>) {
/**
* collector = {
* Log.e("TAG", "collect: $it")
* }
*/
collector.block()
}
}
冷流的实现原理还是比较简单的,flow函数会创建一个SafeFlow的对象,将block参数传入到SafeFlow中。当调用SafeFlow的collect方法时,会调用到collectSafely。在collectSafely中就直接调用了block函数对象。collect方法也需要传入一个collector: FlowCollector<T>参数,就是collector调用了block,然后再block函数对象中又调用了collector.emit发送数据。
简化一点就是collect调用了block,block中调用了emit又会调回来到collector中。
所以冷流的emit会在collect之后才会执行,因为是collect中调用的emit。
热流
SharedFlow简单介绍
kotlin
val flow = MutableSharedFlow<Int>()
// 接收
launch {
flow.collect {
println(it)
}
}
// 发送
launch {
flow.emit(1)
flow.emit(2)
}
可以看到创建SharedFlow时,没有像创建冷流flow一样传入一个block的函数参数。自然调用collect也没有可以重新运行的代码。
SharedFlow原理
kotlin
public fun <T> MutableSharedFlow(
replay: Int = 0,
extraBufferCapacity: Int = 0,
onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND
): MutableSharedFlow<T> {
val bufferCapacity0 = replay + extraBufferCapacity
val bufferCapacity = if (bufferCapacity0 < 0) Int.MAX_VALUE else bufferCapacity0 // coerce to MAX_VALUE on overflow
return SharedFlowImpl(replay, bufferCapacity, onBufferOverflow)
}
SharedFlow的实现类是SharedFlowImpl,collect方法和冷流的实现不一样。
kotlin
override suspend fun collect(collector: FlowCollector<T>): Nothing {
val slot = allocateSlot()
try {
if (collector is SubscribedFlowCollector) collector.onSubscription()
val collectorJob = currentCoroutineContext()[Job]
// 一直循环的监听是否有新数据
while (true) {
var newValue: Any?
while (true) {
// 循环获取新值
newValue = tryTakeValue(slot) // attempt no-suspend fast path first
if (newValue !== NO_VALUE) break
awaitValue(slot) // await signal that the new value is available
}
collectorJob?.ensureActive()
// 给消费者发送新值
collector.emit(newValue as T)
}
} finally {
freeSlot(slot)
}
}
sharedFlow的collect有两层循环,内层循环在不断的获取新值,如果获取到新值将会跳出内层循环,然后给消费者发送新值,之后有开始循环,一直监听下去。
tryTakeValue最终会调用到getPeekedValueLockedAt从buffer中获取数据,buffer是一个数组。emit发送的数据都保存在这个数组中。
kotlin
private fun getPeekedValueLockedAt(index: Long): Any? =
when (val item = buffer!!.getBufferAt(index)) {
is Emitter -> item.value
else -> item
}
tryEmit不会挂起发送方,流程要简单些,先从tryEmit说起。
kotlin
override fun tryEmit(value: T): Boolean {
var resumes: Array<Continuation<Unit>?> = EMPTY_RESUMES
val emitted = synchronized(this) {
// 保存value到buffer中
if (tryEmitLocked(value)) {
resumes = findSlotsToResumeLocked(resumes)
true
} else {
false
}
}
for (cont in resumes) cont?.resume(Unit)
return emitted
}
tryEmitLocked会调用到enqueueLocked保存value到buffer数组中。
kotlin
private fun enqueueLocked(item: Any?) {
val curSize = totalSize
val buffer = when (val curBuffer = buffer) {
null -> growBuffer(null, 0, 2)
else -> if (curSize >= curBuffer.size) growBuffer(curBuffer, curSize,curBuffer.size * 2) else curBuffer
}
// 保存value
buffer.setBufferAt(head + curSize, item)
}
SharedFlow的emit方法会先调用tryEmit,tryEmit返回false才会调用<font style="color:#bcbec4;background-color:#191a1c;">emitSuspen</font>
kotlin
override suspend fun emit(value: T) {
if (tryEmit(value)) return // fast-path
emitSuspend(value)
}
emitSuspend是通过suspendCancellableCoroutine来实现挂起的,创建了一个Emitter对象添加到了SharedFlow的buffer数组中。当collect执行时,会调用到updateCollectorIndexLocked,最后拿到Continuation来恢复协程。
kotlin
private suspend fun emitSuspend(value: T) = suspendCancellableCoroutine<Unit> sc@{ cont ->
var resumes: Array<Continuation<Unit>?> = EMPTY_RESUMES
val emitter = synchronized(this) lock@{
// recheck buffer under lock again (make sure it is really full)
if (tryEmitLocked(value)) {
cont.resume(Unit)
resumes = findSlotsToResumeLocked(resumes)
return@lock null
}
// add suspended emitter to the buffer
// 创建Emitter
Emitter(this, head + totalSize, value, cont).also {
// 添加Emitter对象到buffer数组中
enqueueLocked(it)
queueSize++ // added to queue of waiting emitters
// synchronous shared flow might rendezvous with waiting emitter
if (bufferCapacity == 0) resumes = findSlotsToResumeLocked(resumes)
}
}
// outside of the lock: register dispose on cancellation
emitter?.let { cont.disposeOnCancellation(it) }
// outside of the lock: resume slots if needed
for (r in resumes) r?.resume(Unit)
}
StateFlow原理
StateFlow的使用和SharedFlow很相似,这里就不重复说了。直接说StateFlow的实现原理。SharedFlow是用一个buffer数组来保存数据的,StateFlow是用value变量来保存的。
StateFlowImpl是SateFlow的实现类,StateFlowImpl实现了MutableStateFlow接口,MutableStateFlow中的value就是用来记录SateFlow的值。
kotlin
public interface MutableStateFlow<T> : StateFlow<T>, MutableSharedFlow<T> {
/**
* The current value of this state flow.
*
* Setting a value that is [equal][Any.equals] to the previous one does nothing.
*
* This property is **thread-safe** and can be safely updated from concurrent coroutines without
* external synchronization.
*/
public override var value: T
/**
* Atomically compares the current [value] with [expect] and sets it to [update] if it is equal to [expect].
* The result is `true` if the [value] was set to [update] and `false` otherwise.
*
* This function use a regular comparison using [Any.equals]. If both [expect] and [update] are equal to the
* current [value], this function returns `true`, but it does not actually change the reference that is
* stored in the [value].
*
* This method is **thread-safe** and can be safely invoked from concurrent coroutines without
* external synchronization.
*/
public fun compareAndSet(expect: T, update: T): Boolean
}
SateFlow的emit和tryEmit都是直接赋值。
kotlin
// StateFlowImpl
override fun tryEmit(value: T): Boolean {
this.value = value
return true
}
override suspend fun emit(value: T) {
this.value = value
}
如果多线程emit数据就存在线程安全问题,所以flow也提供了多线程更新数据的方法(update)
kotlin
public inline fun <T> MutableStateFlow<T>.update(function: (T) -> T) {
while (true) {
val prevValue = value
val nextValue = function(prevValue)
if (compareAndSet(prevValue, nextValue)) {
return
}
}
}