[英]How can I send items to a Kotlin.Flow (like a Behaviorsubject)
我想知道如何向Kotlin.Flow
發送/發送項目,所以我的用例是:
在消費者/ViewModel/Presenter 中,我可以使用collect
函數進行訂閱:
fun observe() {
coroutineScope.launch {
// 1. Send event
reopsitory.observe().collect {
println(it)
}
}
}
但問題在於Repository
方面,使用 RxJava,我們可以使用Behaviorsubject將其公開為Observable/Flowable
並發出如下新項目:
behaviourSubject.onNext(true)
但是每當我建立一個新流程時:
flow {
}
我只能收集。 如何將值發送到流?
如果您想獲得訂閱/收藏的最新值,您應該使用ConflatedBroadcastChannel :
private val channel = ConflatedBroadcastChannel<Boolean>()
這將復制BehaviourSubject
,以將通道公開為流:
// Repository
fun observe() {
return channel.asFlow()
}
現在向暴露的Flow
發送一個事件/值,簡單地發送到這個通道。
// Repository
fun someLogicalOp() {
channel.send(false) // This gets sent to the ViewModel/Presenter and printed.
}
安慰:
錯誤的
如果您希望僅在開始收集后接收值,則應改用BroadcastChannel
。
表現為 Rx 的PublishedSubject
private val channel = BroadcastChannel<Boolean>(1)
fun broadcastChannelTest() {
// 1. Send event
channel.send(true)
// 2. Start collecting
channel
.asFlow()
.collect {
println(it)
}
// 3. Send another event
channel.send(false)
}
錯誤的
當第一個事件在collect { }
之前發送時,只會打印false
。
表現為 Rx 的BehaviourSubject
private val confChannel = ConflatedBroadcastChannel<Boolean>()
fun conflatedBroadcastChannelTest() {
// 1. Send event
confChannel.send(true)
// 2. Start collecting
confChannel
.asFlow()
.collect {
println(it)
}
// 3. Send another event
confChannel.send(false)
}
真的
錯誤的
這兩個事件都被打印出來,你總是得到最新的值(如果有的話)。
另外,想提一下 Kotlin 在DataFlow
上的團隊開發(名稱待定):
這似乎更適合這個用例(因為它將是一個冷流)。
看看MutableStateFlow文檔,因為它是ConflatedBroadcastChannel
的替代品,很快就會被棄用。
要獲得更好的上下文,請查看關於 Github 上 Kotlin 存儲庫原始問題的整個討論。
更新:
Kotlin Coroutines 1.4.0
現在可用於MutableSharedFlow
,它取代了對Channel
的需要。 MutableSharedFlow
清理也是內置的,所以你不需要手動打開和關閉它,不像Channel
。 如果您需要類似主題的 api 用於Flow
請使用MutableSharedFlow
原答案
由於您的問題有android
標記,我將添加一個 Android 實現,允許您輕松創建處理其自身生命周期的BehaviorSubject
或PublishSubject
。
這在 Android 中很重要,因為您不想忘記關閉通道並泄漏內存。 此實現通過將反應流與 Fragment/Activity 的創建和銷毀聯系起來,避免了顯式“處置”反應流的需要。 類似於LiveData
interface EventReceiver<Message> {
val eventFlow: Flow<Message>
}
interface EventSender<Message> {
fun postEvent(message: Message)
val initialMessage: Message?
}
class LifecycleEventSender<Message>(
lifecycle: Lifecycle,
private val coroutineScope: CoroutineScope,
private val channel: BroadcastChannel<Message>,
override val initialMessage: Message?
) : EventSender<Message>, LifecycleObserver {
init {
lifecycle.addObserver(this)
}
override fun postEvent(message: Message) {
if (!channel.isClosedForSend) {
coroutineScope.launch { channel.send(message) }
} else {
Log.e("LifecycleEventSender","Channel is closed. Cannot send message: $message")
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
fun create() {
channel.openSubscription()
initialMessage?.let { postEvent(it) }
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun destroy() {
channel.close()
}
}
class ChannelEventReceiver<Message>(channel: BroadcastChannel<Message>) :
EventReceiver<Message> {
override val eventFlow: Flow<Message> = channel.asFlow()
}
abstract class EventRelay<Message>(
lifecycle: Lifecycle,
coroutineScope: CoroutineScope,
channel: BroadcastChannel<Message>,
initialMessage: Message? = null
) : EventReceiver<Message> by ChannelEventReceiver<Message>(channel),
EventSender<Message> by LifecycleEventSender<Message>(
lifecycle,
coroutineScope,
channel,
initialMessage
)
通過使用 Android 的Lifecycle
庫,我現在可以創建一個BehaviorSubject
,在活動/片段被銷毀后自行清理
class BehaviorSubject<String>(
lifecycle: Lifecycle,
coroutineScope: CoroutineScope,
initialMessage = "Initial Message"
) : EventRelay<String>(
lifecycle,
coroutineScope,
ConflatedBroadcastChannel(),
initialMessage
)
或者我可以使用緩沖的BroadcastChannel
創建一個PublishSubject
class PublishSubject<String>(
lifecycle: Lifecycle,
coroutineScope: CoroutineScope,
initialMessage = "Initial Message"
) : EventRelay<String>(
lifecycle,
coroutineScope,
BroadcastChannel(Channel.BUFFERED),
initialMessage
)
現在我可以做這樣的事情
class MyActivity: Activity() {
val behaviorSubject = BehaviorSubject(
this@MyActivity.lifecycle,
this@MyActivity.lifecycleScope
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (savedInstanceState == null) {
behaviorSubject.eventFlow
.onEach { stringEvent ->
Log.d("BehaviorSubjectFlow", stringEvent)
// "BehaviorSubjectFlow: Initial Message"
// "BehaviorSubjectFlow: Next Message"
}
.flowOn(Dispatchers.Main)
.launchIn(this@MyActivity.lifecycleScope)
}
}
override fun onResume() {
super.onResume()
behaviorSubject.postEvent("Next Message")
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.