Reputation: 7089
Recently I discovered a flow behaviour that I couldn't understand.
Consider this situation:
you have a parent flow
and inside its collect
, you'll have a "child" flow
and calling .collect()
, like this:
parentFlow.collect {
childFlow.collect()
}
Then if the parentFlow emits some values, the childFlow.collect()
will not be called.
By doing some searches on SO I found the following questions are similar to mine:
However I intend to dig deeper about what's the reason behind this behaviour, therefore I have created a project to reliably reproduce it, you can check it out on github: https://github.com/dumbfingers/Playground/tree/flow-inside-flow
In this mini-repro, I have put some log output. When you click the button, the expect log output should be:
Test: onArrayUpdated
Test: item: {some number}
Test: item: {some number}
Test: onFlowChanged
Test: repoObserve delayed
Test: item: {some number}
Test: item: {some number}
Test: repoAnotherObserve delayed
However, the actual result is:
Test: onArrayUpdated
Test: item: {some number}
Test: item: {some number}
Which indicates these two collect
call inside the randomFlow
are not called:
repository.repoObserve(list).collect {
repository.repoAnotherObserve().collect()
}
In this SO: Android: collecting a Kotlin Flow inside another not emitting The answer suggested that "collecting infinite flow" cause this issue.
And in my experiment, either
repoObserve
and repoAnotherObserve
to suspend
method and making them not returning a flowor
combine
to combine these flowswill solve this problem.
But why does collect flow inside another flow's collect won't work?
Upvotes: 4
Views: 2710
Reputation: 93541
Your click listener generates the exact same array list of two numbers each time it’s clicked because you are using a new Random instance with the same seed value of 10 each time. You’re passing them to a StateFlow, and StateFlows have the same behavior as distinctUntilChanged()
. They won’t emit a value that is equal to the last emitted value.
I think if you remove the parameter from your Random constructor call, you will get your expected output. This is because your innermost-called flow is not infinite like in the questions you linked.
Upvotes: 0
Reputation: 2719
You can launch a Coroutine inside parent collect { ... } block
val scope: CoroutineScope = ...
parentFlow.collect {
scope.launch {
childFlow.collect()
}
}
or
parentFlow.flatMapMerge { childFlow }.collect {
// logic here ...
}
You can also replace flatMapMerge
with flatMapLatest
/flatMapConcat
/flatMapFirst
(FlowExt library)
Hope to help you
Upvotes: 3