0

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?

VolodymyrH
  • 1,927
  • 2
  • 18
  • 47

1 Answers1

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