flamyoad
flamyoad

Reputation: 565

Collecting from Flow in UI with repeatOnLifeCycle

I started to replace LiveData with Flow since it is more flexible. But then I find out you need to write enormous amount of boilerplate code to observe from Flow in UI.

In the StateFlow documentation, it says that

LiveData.observe() automatically unregisters the consumer when the view goes to the STOPPED state, whereas collecting from a StateFlow or any other flow does not stop collecting automatically. To achieve the same behavior,you need to collect the flow from a Lifecycle.repeatOnLifecycle block.

It's also mentioned in the article by Manuel Vivo that using collecting from lifecycleScope.launchWhenX is dangerous and should not be used in UI because the producer flow will not stop emitting.

He recommended us to use

// Listen to multiple flows
lifecycleScope.launch {
    lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
        // As collect is a suspend function, if you want to collect
        // multiple flows in parallel, you need to do so in 
        // different coroutines
        launch {
            flow1.collect { /* Do something */ }   
        }
                
        launch {
            flow2.collect { /* Do something */ }
        }
    }   
}

The amount of boilerplate code is too much. Is it not possible to do it in a two liner like what LiveData does?

viewModel.movieData.observe(viewLifecycleOwner) {
    ...
}

Why is it so complex to collect from Flow in UI? Is it advisable to convert the Flow to LiveData with asLiveData()?

Upvotes: 4

Views: 3580

Answers (2)

Siri
Siri

Reputation: 941

First answer your first question: Flow is a cold flow. And Flow is stateless. If you provide Flow, then it means that you need to construct and collect Flow frequently.

In another case, if Hot Flow is provided, such as (StateFlow), although the hot flow provides state (.value), it does not know anything about the life cycle of Android. As you said, you can use launchWhenXXX() to collect Flow.

When using launchWhenXXX(), you must pay attention to the life cycle of the hot flow. When to start collect and when to end collect, these need to be paid attention to. So it seems very troublesome. Of course, Flow is a way to get rid of using LiveData.

For details, please refer to: https://proandroiddev.com/should-we-choose-kotlins-stateflow-or-sharedflow-to-substitute-for-android-s-livedata-2d69f2bd6fa5

The second question: LiveData manages the life cycle of Android. Flow.asLiveData() is completely desirable. At this time, only a simple Observe is needed.

Upvotes: 1

Amandeep Singh
Amandeep Singh

Reputation: 11

You could build extensions to reduce the boilerplate

inline fun <T> Flow<T>.collectIn(
    owner: LifecycleOwner,
    minActiveState: Lifecycle.State = Lifecycle.State.STARTED,
    coroutineContext: CoroutineContext = EmptyCoroutineContext,
    crossinline action: suspend CoroutineScope.(T) -> Unit
) = owner.addRepeatingJob(minActiveState, coroutineContext) {
    collect {
        action(it)
    }
}

This makes collecting flows similar to LiveData as

flow.collectIn(viewLifecycleOwner){ /* do stuff */ }

Upvotes: 1

Related Questions