Search code examples
androidkotlinkotlin-flowkotlin-stateflowkotlin-sharedflow

Flow emits different values when collecting it multiple times


I created a Flow from which I emit data. When I collect this flow twice, there are 2 different sets of data emitted from the same variable instead of emitting the same values to both collectors.

I have a simple Flow that I created myself. The text will be logged twice a second

    val demoFlow: Flow<String> = flow {
        while (true) {
            val text = "Flow ${(0..100).random()}"
            Log.d("TAG", text)
            emit(text)
            delay(1000)
        }
    }

In my viewModel I have a simple function that gets the previous Flow

fun getSimpleFlow() = FlowRepository.demoFlow

And in my Fragment I collect and display my Flow

        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                launch {
                    viewModel.getSimpleFlow().collect {
                        binding.tv1.text = it
                    }
                }
                launch {
                    viewModel.getSimpleFlow().collect {
                        binding.tv2.text = it
                    }
                }
            }
        }

If I transform the Flow to a StateFlow or a SharedFlow, I no longer have this problem. I don't understand how or why this happens since I'm using the same 'demoFlow' variable. Is there a way to get the same values from 'demoFlow' without converting to a StateFlow or a SharedFlow?


Solution

  • Regular Flows are cold, this behaviour is by design.

    The demoFlow is the same, so you have the same Flow instance. However, collecting the flow multiple times actually runs the body inside the flow { ... } definition every time from the start. Each independent collection has its own variable i etc.

    Using a StateFlow or a SharedFlow allows to share the source of the flow between multiple collectors. If you use shareIn or stateIn on some source flow, that source flow is only collected once, and the items collected from this source flow are shared and sent to every collector of the resulting state/shared flow. This is why it behaves differently.

    In short, reusing a Flow instance is not sufficient to share the collection. You need to use flow types that are specifically designed for this.