I used a PublishSubject and I was sending messages to it and also I was listening for results. It worked flawlessly, but now I'm not sure how to do the same thing with Kotlin's coroutines (flows or channels).
private val subject = PublishProcessor.create<Boolean>>()
...
fun someMethod(b: Boolean) {
subject.onNext(b)
}
fun observe() {
subject.debounce(500, TimeUnit.MILLISECONDS)
.subscribe { /* value received */ }
}
Since I need the debounce operator I really wanted to do the same thing with flows so I created a channel and then I tried to create a flow from that channel and listen to changes, but I'm not getting any results.
private val channel = Channel<Boolean>()
...
fun someMethod(b: Boolean) {
channel.send(b)
}
fun observe() {
flow {
channel.consumeEach { value ->
emit(value)
}
}.debounce(500, TimeUnit.MILLISECONDS)
.onEach {
// value received
}
}
What is wrong?
Flow
is a cold asynchronous stream, just like an Observable
.
All transformations on the flow, such as
map
andfilter
do not trigger flow collection or execution, only terminal operators (egsingle
) do trigger it.
The onEach
method is just a transformation. Therefore you should replace it with the terminal flow operator collect
. Also you could use a BroadcastChannel
to have cleaner code:
private val channel = BroadcastChannel<Boolean>(1)
suspend fun someMethod(b: Boolean) {
channel.send(b)
}
suspend fun observe() {
channel
.asFlow()
.debounce(500)
.collect {
// value received
}
}
Update: At the time the question was asked there was an overload of debounce
with two parameters (like in the question). There is not anymore. But now there is one which takes one argument in milliseconds (Long).
ArrayBroadcastChannel in Kotlin coroutines is the one most similar to PublishSubject.
Unlike PublishSubject, backpressure is inbuilt into the coroutine channels, and that is where the buffer capacity comes in. This number really depends on which use case the channel is being used for. For most of the normal use cases, I just go with 10, which should be more than enough. If you push events faster to this channel than receivers consuming it, you can give more capacity.
It should be SharedFlow/MutableSharedFlow
for PublishProcessor/PublishRelay
and StateFlow/MutableStateFlow
for BehaviorProcessor/BehaviorRelay
.
// initial value to constructor
private val _myFlow = MutableStateFlow<Boolean>("")
// expose as Flow<Boolean>
val myFlow = _myFlow
...
fun someMethod(b: Boolean) {
_myFlow.value = b
}
// this must be either `suspend` fun or create coroutine (`.launch{}`) inside to be able to `.collect`
suspend fun observe() {
myFlow.debounce(500)
.collect { }
}
MutableStateFlow
uses .equals
comparison when setting new value, so it does not emit same value again and again (versus distinctUntilChanged
which uses referential comparison)
Edit : I am not sure about difference of _myFlow.value=b
vs _myFlow.emit(b)
. Someone enlighten
Actually BroadcastChannel
is obsolete already, Jetbrains changed their approach to use SharedFlows
instead. Which is a lot more cleaner, easier to implement and solves a lot of pain points.
Essentially, you can achieve the same thing like this.
class BroadcastEventBus {
private val _events = MutableSharedFlow<Event>()
val events = _events.asSharedFlow() // read-only public view
suspend fun postEvent(event: Event) {
_events.emit(event) // suspends until subscribers receive it
}
}
To read about it more, checkout Roman's Medium article.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.