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
Jobreferences 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
blockin 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 aFlowcontinuously 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
repeatOnLifecycleis suspend, so you need to call it inside a coroutine. - Choosing the wrong state
If you pickRESUMEDbut you want updates even when the UI is partially obscured, you might miss events. - Using
lifecycleinstead ofviewLifecycleOwner.lifecyclein Fragments
Always useviewLifecycleOwnerto 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 preferLaunchedEffectorcollectAsStateWithLifecycle, which build on similar principles — but for classic Views,repeatOnLifecycleis your best friend.
