In general, Flows run on the thread/coroutine context that
collects them, because under-the-hood a call to
flow { emit() }
directly executes the lambda passed to
flow.collect { }
. Of course things get much more complicated when it adds operators,
.flowOn
, etc, but this is the general principle of Flows. For example:
val collector = FlowCollector<Unit> { }
flow<Unit> {
// `this === collector` is true
emit(Unit)
}.collect(collector)
But any callbacks that get registered within that flow will probably be running on a different context, since it’s a completely separate API or async mechanism that will eventually invoke the lambda. So the only way to guarantee values from one callback-based API match the coroutine context of the emitter/collector, is to pass the values through a Channel, which is designed to facilitate communication between different coroutine contexts. Conceptually, this is basically what a callbackFlow is doing (though again, the details are a bit more complicated):
val channel = Channel<Unit>(Channel.BUFFERED, BufferOverflow.SUSPEND)
withFrameMillis {
channel.trySend(Unit)
}
flow<Unit> {
for(value in channel) {
emit(value)
}
}