[英]How do you split a 'hot' stream of events from a callback in Kotlin?
I'm processing a hot stream of events, arriving by callback.我正在处理一个热的 stream 事件,通过回调到达。 'Downstream' I'd like to split it into multiple streams, and process them.The events all arrive sequentially from a single thread (which I don't control, so I don't think I can use co routines here) What is the right structure to use here?
'下游'我想把它分成多个流,并处理它们。事件都是从一个线程顺序到达的(我不控制它,所以我认为我不能在这里使用协程) 什么是在这里使用正确的结构?
I can create a Flow pretty easily, using callbackFlow and sendBlocking, but the semantics don't seem to line up, as the Flow isn't cold.我可以使用 callbackFlow 和 sendBlocking 非常轻松地创建流,但语义似乎不一致,因为流并不冷。 What is the best way to split a flow into multiple downstream flows (depending on the contents of events).
将流拆分为多个下游流的最佳方法是什么(取决于事件的内容)。 Or should I use channels?
还是我应该使用频道? It matches the 'hotness' of my source, but the whole polling downstream seems off (in this basically synchronoussituation), and a lot of the methods seem deprecated in favor of Flow.
它与我的来源的“热度”相匹配,但整个下游轮询似乎关闭(在这种基本上同步的情况下),并且许多方法似乎已被弃用以支持 Flow。
I can do all this by just using 'callbacks all the way' but that creates a lot tighter coupling than I'd like.我可以通过只使用“一路回调”来完成所有这些,但这会产生比我想要的更紧密的耦合。 Any ideas?
有任何想法吗?
Edit:编辑:
I ended up with this, seems to work:我结束了这个,似乎工作:
fun testFlow() {
runBlocking {
val original = flowOf("aap", "noot", "mies", "wim", "zus","jet","weide","does")
val broadcast = original.broadcastIn(this)
val flow1 = broadcast.openSubscription().receiveAsFlow().filter { it.length == 4 }
val flow2 = broadcast.openSubscription().receiveAsFlow().filter { it.length == 3 }
flow1.collect { it -> println("Four letter: ${it}") }
flow2.collect { it -> println("Three letter: ${it}") }
}
}
There will soon be a hot SharedFlow
for this use case, but in the meantime you can use a BroadcastChannel
under the cover.这个用例很快就会有一个热门的
SharedFlow
,但与此同时,您可以在幕后使用BroadcastChannel
。
You can start with callbackFlow
to create a cold flow from a callback based API (see Roman Elizarov's post about it ).您可以从 callbackFlow 开始,从基于 API 的
callbackFlow
创建冷流(请参阅 Roman Elizarov 的相关帖子)。 Then use the following to make it hot and share it:那就用下面的来火热分享一下吧:
val original: Flow<String> = TODO("get original flow")
// create an implicit hot BroadcastChannel, shared between collectors
val sharedFlow = original.broadcastIn(scope).asFlow()
// create derived cold flows, which will subscribe (on collect) to the
// same hot source (BroadcastChannel)
val flow1 = sharedFlow.filter { it.length == 4 }
val flow2 = sharedFlow.filter { it.length == 3 }.map { it.toUppercase() }
flow1.collect { it -> println("Four letter: ${it}") }
flow2.collect { it -> println("Three letter: ${it}") }
First to clarify, even if Flow
s are mostly cold for now, there is already a hot StateFlow
, and there will soon be a convenient share
operator and a hot SharedFlow
to simplify this kind of use case.首先澄清一下,即使
Flow
现在大部分都是冷的,但已经有一个热的StateFlow
,并且很快就会有一个方便的share
运算符和一个热的SharedFlow
来简化这种用例。
While we wait for this, if you initially have a cold Flow
, you currently have to first create a hot channel (and a coroutine to send elements to it) from which we derive flows sharing the hot source.当我们等待这个时,如果您最初有一个
Flow
,您目前必须首先创建一个热通道(和一个协程来向它发送元素),我们从中派生共享热源的流。 This can easily be done in one of these ways:这可以通过以下方式之一轻松完成:
Flow.produceIn(scope)
launches a coroutine in the given scope and gives you a ReceiveChannel
(useful for fan-out, see below) Flow.produceIn(scope)
在给定的 scope 中启动协程并为您提供ReceiveChannel
(对于扇出很有用,请参见下文)Flow.broadcastIn(scope)
launches a coroutine in the given scope and gives you a BroadcastChannel
(useful for actual sharing, see below) Flow.broadcastIn(scope)
在给定的 scope 中启动协程并为您提供BroadcastChannel
(对于实际共享很有用,请参见下文) Once you have a hot channel, you can convert it into a flow and get different behaviours:拥有热通道后,您可以将其转换为流并获得不同的行为:
ReceiveChannel.consumeAsFlow()
creates a Flow
from a hot source, but it can only be collect
-ed by a single collector (throws otherwise) ReceiveChannel.consumeAsFlow()
从热源创建一个Flow
,但它只能由单个collect
器收集(否则抛出)ReceiveChannel.receiveAsFlow()
creates a multi-collector Flow
, but it behaves in a fan-out fashion (each element form the source channel only goes to one consumer) ReceiveChannel.receiveAsFlow()
创建一个多收集器Flow
,但它以扇出方式运行(来自源通道的每个元素仅进入一个消费者)BroadcastChannel.asFlow()
creates a multi-collector Flow
where each collector gets all elements (which is effectively sharing). BroadcastChannel.asFlow()
创建一个多收集器Flow
,其中每个收集器获取所有元素(有效共享)。 Calling collect
creates a new subscription on the BroadcastChannel
, and handles cancellation properly.collect
在BroadcastChannel
上创建一个新订阅,并正确处理取消。StateFlow
StateFlow
的“最新状态”语义This is not your use case, but sometimes you may not want necessarily all values in a flow, but rather the latest current state and state updates.这不是您的用例,但有时您可能不需要流程中的所有值,而是最新的当前 state 和 state 更新。
This used to be done via a ConflatedBroadcastChannel
, but you can now use a StateFlow
to represent this (since coroutines 1.3.6):这过去是通过
ConflatedBroadcastChannel
完成的,但您现在可以使用StateFlow
来表示它(自协程 1.3.6 起):
MutableStateFlow
MutableStateFlow
的值equal
ity).equal
性)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.