I need to run a task, which emits some data. I want to subscribe to this data like PublishSubject. But I can't solve a problem of one-instance flow. If I try to call it again, it will create another instance and the job will be done twice. I tried to run the flow internally and post values to the BroadcastChannel, but this solution doesn't seem correct. What is the best practice for such a task?
Asked
Active
Viewed 80 times
0
-
do you want it to be as Hot Stream? this might help https://medium.com/@elizarov/cold-flows-hot-channels-d74769805f9 – Sergio Jul 07 '20 at 13:59
-
I think you might be waiting for this feature: https://github.com/Kotlin/kotlinx.coroutines/issues/2034 – Carson Holzheimer Jul 12 '20 at 09:50
1 Answers
0
This will do the magic:
fun <T> Flow<T>.refCount(capacity: Int = Channel.CONFLATED, dispatcher: CoroutineDispatcher = Dispatchers.Default): Flow<T> {
class Context(var counter: Int) {
lateinit var job: Job
lateinit var channel: BroadcastChannel<T>
}
val context = Context(0)
fun lock() = synchronized(context) {
if (++context.counter > 1) {
return@synchronized
}
context.channel = BroadcastChannel(capacity)
context.job = GlobalScope.async(dispatcher) {
try {
collect { context.channel.offer(it) }
} catch (e: Exception) {
context.channel.close(e)
}
}
}
fun unlock() = synchronized(context) {
if (--context.counter == 0) {
context.job.cancel()
}
}
return flow {
lock()
try {
emitAll(context.channel.openSubscription())
} finally {
unlock()
}
}
}

VolodymyrH
- 1,927
- 2
- 18
- 47