If you’ve been working with Kotlin coroutines in Android, you’ve probably faced the challenge of running tasks that automatically start and stop depending on the lifecycle state of your Activity or Fragment. That’s exactly where repeatOnLifecycle
comes in — a coroutine API that helps you run code only when your UI is in a certain state, without leaking resources.
In this post, we’ll break down what repeatOnLifecycle
is, why it exists, how it works, and how to use it properly.
Why Do We Even Need repeatOnLifecycle
?
In Android, Activities and Fragments have lifecycle states like CREATED
, STARTED
, and RESUMED
.
If you’re collecting data from a Flow
or running a coroutine, you don’t always want it to run 24/7 — especially if the UI is not visible.
Before repeatOnLifecycle
, developers often:
- Launched coroutines in
onStart()
and manually canceled them inonStop()
. - Managed
Job
references and cleanup code manually. - Risked memory leaks or wasted processing power if cleanup wasn’t done correctly.
repeatOnLifecycle
solves this pain.
It automatically starts your block of code when the lifecycle reaches a target state and cancels it when the state drops below that.
What Exactly Is repeatOnLifecycle
?
repeatOnLifecycle
is a suspend function introduced in androidx.lifecycle
that works with LifecycleOwner
(like Activities and Fragments).
suspend fun LifecycleOwner.repeatOnLifecycle(
state: Lifecycle.State,
block: suspend CoroutineScope.() -> Unit
)
What it does:
- Suspends until the lifecycle reaches the given
state
. - Runs the provided
block
in a new coroutine. - Cancels the coroutine when the lifecycle goes below the
state
. - Restarts the coroutine when the lifecycle comes back to that state.
Lifecycle States Recap
Here are the main states you’ll usually use with repeatOnLifecycle
:

Lifecycle.State.CREATED
→ Component is created, but UI might not be visible.Lifecycle.State.STARTED
→ UI is visible (but may not be interactive).Lifecycle.State.RESUMED
→ UI is visible and interactive.
For most UI data collection (like observing ViewModel state), STARTED
is the go-to choice.
Basic Usage Example
Let’s see a practical example:
class MyFragment : Fragment(R.layout.fragment_my) {
private val viewModel: MyViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect { state ->
// Update UI based on state
binding.textView.text = state.message
}
}
}
}
}
Here,
lifecycleScope.launch { ... }
→ Starts a coroutine tied to the Fragment’s lifecycle.viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { ... }
→ Runs the block only when the Fragment is visible.viewModel.uiState.collect { ... }
→ Collects from aFlow
continuously while visible.- When the Fragment is paused or stopped, collection stops automatically.
- When it’s visible again, collection restarts — no manual cleanup needed.
Key Benefits of repeatOnLifecycle
- Lifecycle awareness
You never run code in a lifecycle state where it doesn’t make sense. - Automatic cancellation and restart
No need to manually handle cleanup inonStop()
oronDestroyView()
. - Memory safety
Prevents leaks caused by coroutines running longer than intended. - Less boilerplate
Your lifecycle handling logic is reduced to a single function call.
Common Pitfalls to Avoid
- Forgetting to wrap it in a
lifecycleScope.launch
repeatOnLifecycle
is suspend, so you need to call it inside a coroutine. - Choosing the wrong state
If you pickRESUMED
but you want updates even when the UI is partially obscured, you might miss events. - Using
lifecycle
instead ofviewLifecycleOwner.lifecycle
in Fragments
Always useviewLifecycleOwner
to avoid collecting when the view is destroyed.
Advanced Tip: Multiple Collectors Inside One Block
You can collect multiple Flows in a single repeatOnLifecycle
call:
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
viewModel.uiState.collect { updateUI(it) }
}
launch {
viewModel.notifications.collect { showNotification(it) }
}
}
}
This way, both collections start and stop together, tied to the same lifecycle state.
Conclusion
repeatOnLifecycle
is one of those APIs that quietly eliminates a ton of messy lifecycle handling code. It helps you:
- Keep your coroutines safe and clean.
- Avoid manual job management.
- Write less boilerplate while staying lifecycle-aware.
If you’re still manually starting and canceling coroutines in onStart()
/onStop()
, it’s time to move on. repeatOnLifecycle
is the modern, safer, and cleaner way to handle lifecycle-aware coroutine work in Android.
Tip:
If you’re already using Jetpack Compose, you might preferLaunchedEffect
orcollectAsStateWithLifecycle
, which build on similar principles — but for classic Views,repeatOnLifecycle
is your best friend.