Difference between launchWhenStarted and repeatOnLifecycle(STARTED) in collecting flows
Asked Answered
K

4

22

As launchWhenStarted and repeatOnLifecycle(STARTED) provide completely different functionality (launchWhenStarted suspends the execution of the coroutine, and repeatOnLifecycle cancels and restarts a new coroutine), if the names of the new APIs were similar (for example, using launchWhenever for the restarting APIs), developers could’ve got confused and even use them interchangeably without noticing.

source

What is a simpler explanation for when to use which?

Kattegat answered 14/2, 2022 at 14:38 Comment(1)
launchWhenStarted suspends the execution of the coroutine, and repeatOnLifecycle cancels and restarts a new coroutine This is the difference. If you have a running coroutine launched with lauchWhen... and the lifecycle goes below that level, the coroutine will be suspended (but will still be alive, so depending what else it does, you may be wasting resources in things you don't need). The newer repeatOn... is "smarter" as it can cancel the coroutine and start a new one when needed. The easiest way to see this, is to try them yourself.Lottery
M
42

launchWhenStarted is just a one-time delay.

repeatOnLifecycle creates a suspending point that acts as a handler that runs provided block every time the lifecycle enters provided state and cancels it whenever it falls below it (so for STARTED it happens when it gets stopped).

Update:

Notice that launchWhenStarted API is now deprecated because it could cause work to hang for a very long time.

Currently for a one-shot one-time delay it's recommended to lifecycleScope.launch() a new job and use withStarted{ } method within it to cause a suspension until started state is reached.

Detailed explanation of deprecation and replacements is in this this google tracker issue.

Munroe answered 14/2, 2022 at 14:54 Comment(0)
G
6

repeatOnLifecycle restarts its coroutine from scratch on each repeat, and cancels it each time lifecycle falls below the specified state. It’s a natural fit for collecting most flows, because it fully cancels the flow when it’s not needed, which saves resources related to the flow continuing to emit values.

launchWhenX doesn’t cancel the coroutine and restart it. It just postpones when it starts, and pauses execution while below the specified state. They plan to deprecate these functions but I suspect there will need to be some replacement if they do, for the case where you are calling some time consuming suspend function and then want to do something when it’s done, like starting a fragment transaction. Using repeatOnLifecycle for this would result in redoing the time consuming action.

Grady answered 14/2, 2022 at 15:5 Comment(3)
"for the case where you are calling some time consuming suspend function and then want to do something when it’s done, like starting a fragment transaction." would it be better to use SharedFlow in this situation?Postdoctoral
@ghiath, possibly, but it would be rather opinionated of the Jetpack library to expect you to use Flows for everything.Grady
@Grady thank you for the answer. I was looking for replacement of launchWhenX when I read their document about removing the said api in the future. I guess launchWhenX is the only option for now.Sweptwing
O
3

A cold flow backed by a channel or using operators with buffers such as buffer, conflate, flowOn, or shareIn is not safe to collect with some of the existing APIs such as CoroutineScope.launch, Flow<T>.launchIn, or LifecycleCoroutineScope.launchWhenX, unless you manually cancel the Job that started the coroutine when the activity goes to the background. These APIs will keep the underlying flow producer active while emitting items into the buffer in the background, and thus wasting resources.

To solve this issue with these APIs, you’d need to manually cancel collection when the view goes to the background. but it sounds to be a boilerplate code.

That's why Google recommended repeatOnLifecycle(Lifecycle.State.XXX) to make it simple and safe.

repeatOnLifecycle is a suspend function that takes a Lifecycle.State as a parameter which is used to automatically create and launch a new coroutine with the block passed to it when the lifecycle reaches that state, and cancel the ongoing coroutine that’s executing the block when the lifecycle falls below the state.

Learn more from here

Orobanchaceous answered 23/6, 2022 at 22:5 Comment(1)
If my ViewModel has a stateIn or shareIn, then the coroutine is scoped to viewModelScope. When my Activity collects the ViewModel's StateFlow, the repeatOnLifecycle is scoped to lifecycleScope. So if I used repeatOnLifecycle(Lifecycle.State.STARTED) and I enter the STOP state, yes the Activity will stop collecting the Flow but no the underlying hot StateFlow won't stop from collecting whatever cold Flow it collects using stateIn. Correct me if I'm wrong but that's what I'm thinking while reading this answer.Anglicanism
H
1

Warning: Prefer collecting flows using the repeatOnLifecycle API instead of collecting inside the launchWhenX APIs. As the latter APIs suspend the coroutine instead of cancelling it when the Lifecycle is STOPPED, upstream flows are kept active in the background, potentially emitting new items and wasting resources.

According to the official documentation: https://developer.android.com/topic/libraries/architecture/coroutines?hl=en

Hairdresser answered 22/6, 2024 at 23:51 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.