Kotlin中flow发射与接收分析

引言

本文源码基于Coroutine1.3.4。Kotlin Coroutine 1.3.2 新增了flow库,通过官方描述:Flow — cold asynchronous stream with flow builder and comprehensive operator set (filter, map, etc). 可知Flow十分类似RxJava中的Obsaverble。下面通过简单例程来看一下它的使用:

fun main() {
    runBlocking {
        flow {
            for (i in 1..5) {
                emit(i)
            }
        }.collect {
            println("$it")
        }
    }
}

这段代码逻辑很简单,但是却包含了Flow上游流的生成以及下游流的收集过程,下面深入看一下这一过程是如何实现的。

上游构造SafeFlow

在上面代码中先是通过flow()函数创建了Flow对象:

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) : Flow<T> {
    override suspend fun collect(collector: FlowCollector<T>) {
        val safeCollector = SafeCollector(collector, coroutineContext)
        try {
            safeCollector.block()
        } finally {
            safeCollector.releaseIntercepted()
        }
    }
}

可以看到flow()函数返回了一个SafeFlow对象,其继承了Flow类,重写了父类的collect()方法。除此之外就没有其他任何操作了,这里我们明白了“cold asynchronous stream ”的意思了,即上游构造Flow对象时其block内部的逻辑是没有执行的,只有在下游调用了SafeFlow的collect()方法后才会执行block中的逻辑。

下游collect()逻辑

在流下游收集时调用的collect()函数并不是SafeFlow中重写的那个函数,实际上中间做了中转,最后还是会调用到SafeFlow中的collect()函数。看一下main()函数中调用的collect()函数的定义:

//FlowKt.class
public suspend inline fun <T> Flow<T>.collect(crossinline action: suspend (value: T) -> Unit): Unit =
    collect(object : FlowCollector<T> {
        override suspend fun emit(value: T) = action(value)
    })

可以看到进一步调用SafeFlow中的collect()函数,其传入的参数是类型为FlowCollector的匿名内部对象,重写父类FlowCollector的 emit(value: T) = action(value),这里的action即为下游collect {}中的逻辑,从这里可以知道当上游调用FlowCollector实现类中的emit(value)方法时,也即会执行下游collect {}中的action(value)逻辑,这也是一个发射即接收的操作,中间不设及任何转换。
重新回到上一节中SafeFlow中collect()函数的代码。其先是构造了一个SafeCollector对象safeCollector,注意其collector参数是这里的FlowCollector的匿名内部对象。接着执行safeCollector.block()即会执行flow {}中的代码,其中逻辑是五次执行emit(i),也就会执行到SafeCollector中的emit()方法:

override suspend fun emit(value: T) {
        return suspendCoroutineUninterceptedOrReturn sc@{ uCont ->
            try {
                emit(uCont, value)
            } catch (e: Throwable) {
                // Save the fact that exception from emit (or even check context) has been thrown
                lastEmissionContext = DownstreamExceptionElement(e)
                throw e
            }
        }
    }

可以看到这里挂起协程,并将当前Continuation对象uCont和value传给另一个emit()函数:

/**
     * This is a crafty implementation of state-machine reusing.
     * First it checks that it is not used concurrently (which we explicitly prohibit) and
     * then just cache an instance of the completion in order to avoid extra allocation on each emit,
     * making it effectively garbage-free on its hot-path.
     */
private fun emit(uCont: Continuation<Unit>, value: T): Any? {
        val currentContext = uCont.context
        // This check is triggered once per flow on happy path.
        val previousContext = lastEmissionContext
        if (previousContext !== currentContext) {
            checkContext(currentContext, previousContext, value)
        }
        completion = uCont
        return emitFun(collector as FlowCollector<Any?>, value, this as Continuation<Unit>)
    }

通过注释可以知道这里巧妙运用了状态机。第一步是检查并发,这里不允许并发;第二步是将Continuation对象保存在completion字段中,避免额外分配;第三步是调用emitFun()进一步处理。

//SafeCollectorKt.class
@Suppress("UNCHECKED_CAST")
private val emitFun =
    FlowCollector<Any?>::emit as Function3<FlowCollector<Any?>, Any?, Continuation<Unit>, Any?>
//FlowCollector.kt
public interface FlowCollector<in T> {
    public suspend fun emit(value: T)
}

这里大概是将FlowCollector中只有一个参数的emit(value: T)方法转换成有三个参数的方法,看一下反编译成Java代码或许更清楚一点:

   private static final Function3 emitFun = (Function3)TypeIntrinsics.beforeCheckcastToFunctionOfArity(new Function3() {
      // $FF: synthetic method
      // $FF: bridge method
      public Object invoke(Object var1, Object var2, Object var3) {
         return this.invoke((FlowCollector)var1, var2, (Continuation)var3);
      }

      @Nullable
      public final Object invoke(@NotNull FlowCollector p1, @Nullable Object p2, @NotNull Continuation continuation) {
         InlineMarker.mark(0);
         Object var10000 = p1.emit(p2, continuation);
         InlineMarker.mark(2);
         InlineMarker.mark(1);
         return var10000;
      }

      public final KDeclarationContainer getOwner() {
         return Reflection.getOrCreateKotlinClass(FlowCollector.class);
      }

      public final String getName() {
         return "emit";
      }

      public final String getSignature() {
         return "emit(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;";
      }
   }, 3);

在invoke()方法中p1是传如的FlowCollector的匿名内部对象,p2即为发射的value值,continuation是传入的续体,最后返回FlowCollector的匿名内部对象emit()方法的执行结果。下面再看一下emit()方法:

public suspend inline fun <T> Flow<T>.collect(crossinline action: suspend (value: T) -> Unit): Unit =
    collect(object : FlowCollector<T> {
        override suspend fun emit(value: T) = action(value)
    })

其进一步调用lambda表达式action,action即是collect {}中的代码,因此会执行 println("$it")。至此一次发射与接收的流程完成,即完成一次for循环的emit过程,接着会重新执行第二次emit过程。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Kotlin StateFlowKotlin协程库的一种流(Flow)实现,用于在异步场景下处理状态的变化。StateFlow可用于代替LiveData或RxJava的Observable,提供了一种简洁且类型安全的方式来观察和响应状态的变化。 StateFlow是一个具有状态的流(flow),它可以发射新的值并保持最新的状态。与普通的Flow相比,StateFlow更适用于表示单一的可变状态,并且可以方便地在多个观察者之间共享。 StateFlow在使用上类似于普通的Flow,你可以使用`stateIn`函数将其转换为一个只读的SharedFlow,并使用`collect`或`conflate`等操作符来观察和处理状态的变化。 下面是一个使用StateFlow的示例代码: ``` import kotlinx.coroutines.* import kotlinx.coroutines.flow.* fun main() = runBlocking { val state = MutableStateFlow("Initial state") val job = launch { state.collect { value -> println("Received state: $value") } } state.value = "Updated state" job.cancel() } ``` 在上面的示例,我们创建了一个MutableStateFlow对象并初始化为"Initial state"。然后使用`collect`函数来观察state的变化,并在状态发生变化时打印出新的值。我们通过修改`state.value`来更新状态,并在控制台上看到"Received state: Updated state"的输出。 总之,Kotlin StateFlow提供了一种方便的方式来处理状态的变化,并与Kotlin协程无缝集成,使得在异步场景下处理状态变化更加简洁和可靠。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值