lifecycleScope — is a built-in CoroutineScope from the androidx.lifecycle library, tied to the lifecycle of an Activity, Fragment, or any LifecycleOwner, and automatically cancels coroutines when the component is destroyed. According to Google Android Developers, 2025, lifecycleScope allows you to safely launch coroutines related to the UI layer without the risk of executing code after the Activity or Fragment is destroyed. The scope is automatically cancelled when the LifecycleOwner transitions to the DESTROYED state.
Key Takeaways
lifecycleScope is an extension property on the LifecycleOwner interface (Activity, Fragment, Service) that provides a ready-made CoroutineScope tied to the full lifecycle of the component. When the LifecycleOwner reaches the DESTROYED state, lifecycleScope automatically cancels all active coroutines.
// In Fragment or Activity
lifecycleScope.launch {
delay(1000)
showSnackbar("Hello!")
}
Unlike viewModelScope, lifecycleScope is cancelled every time the LifecycleOwner is destroyed — including screen rotation. This makes it ideal for operations that should only live while a specific screen is visible.
lifecycleScope is available everywhere there is a LifecycleOwner:
The automatic cancellation mechanism of lifecycleScope is based on subscribing to Lifecycle events. When the Lifecycle drops below CREATED to DESTROYED, the scope is cancelled.
| State | Description | Scope Active |
|---|---|---|
| CREATED | LifecycleOwner created, onCreate executed | Yes |
| STARTED | LifecycleOwner visible (onStart) | Yes |
| RESUMED | LifecycleOwner in the foreground (onResume) | Yes |
| DESTROYED | LifecycleOwner destroyed (onDestroy) | No (scope cancelled) |
lifecycleScope is created as CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) and stored inside the Lifecycle. When the Lifecycle transitions to the DESTROYED state, scope.cancel() is called. The mechanism is implemented via LifecycleEventObserver, which subscribes to lifecycle events on the first access to the scope.
When the screen is rotated, the Activity is destroyed (onDestroy) and recreated. lifecycleScope is cancelled together with the old Activity, and a new scope instance is created for the new Activity. This is a fundamental difference from viewModelScope, which survives rotation.
The lifecycle library provides several ways to launch coroutines through lifecycleScope. Let’s look at the evolution of the API from deprecated methods to modern ones.
The simplest way is lifecycleScope.launch { ... }. The coroutine starts immediately and is cancelled at DESTROYED. However, it can execute code even when the UI is not visible (e.g., in the background after onStop). This is not always desirable.
These methods paused the coroutine execution when the Lifecycle dropped below the specified state and resumed it upon return. However, they were marked as @Deprecated in lifecycle-runtime-ktx 2.6.0 because:
repeatOnLifecycle is Google’s recommended way to launch coroutines synchronized with the lifecycle. It cancels and restarts the coroutine every time the Lifecycle reaches the specified state.
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect { state ->
updateUI(state)
}
}
}
The coroutine passed to repeatOnLifecycle starts when the Lifecycle reaches STARTED and cancels when it drops below STARTED. When it returns to STARTED, the coroutine restarts from scratch. This is safe and efficient — no coroutines are left suspended.
For collecting data from Flow with lifecycle awareness, there is the flowWithLifecycle operator. It automatically stops and resumes collection when the Lifecycle state changes:
viewModel.uiState
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.onEach { state -> updateUI(state) }
.launchIn(lifecycleScope)
The flowWithLifecycle operator is the most concise way to safely subscribe to a Flow in the UI layer.
Let’s look at three real-world scenarios for using lifecycleScope in an Android application with Kotlin.
class MapFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
locationProvider.observeLocation().collect { loc ->
updateMapMarker(loc)
}
}
}
}
}
The coroutine starts when the fragment becomes visible (STARTED) and cancels when it leaves the screen (STOPPED). If the user switches to another app, Location updates do not drain the battery.
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
animateFadeIn(titleView)
delay(200)
animateSlideUp(contentView)
}
}
The animation runs only when the fragment is in the foreground (RESUMED). If the user minimizes the app during the animation, the coroutine is cancelled, and when they return, the animation starts over.
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
while (isActive) {
syncData()
delay(30_000L)
}
}
}
Data is synchronized every 30 seconds, but only when the screen is visible. isActive checks whether the coroutine has been cancelled, providing a safe way to exit the loop when leaving the screen.
Both scopes are tied to the lifecycle, but to different aspects of it. Understanding the difference is critical for building a proper Android application architecture.
viewModelScope is tied to the ViewModel, which survives screen rotation. lifecycleScope is tied to the LifecycleOwner (Activity/Fragment), which is destroyed and recreated on rotation. This determines their use cases.
In practice, a combination of both scopes is common: viewModelScope loads data and manages state, while lifecycleScope subscribes to Flow from the ViewModel with lifecycle awareness. This separation of responsibilities is considered best practice in modern Android development.
Let’s look at four of the most common mistakes developers make when using lifecycleScope.
If you launch data loading in lifecycleScope.launch, the coroutine will be cancelled on screen rotation, and the data will have to be reloaded. Use viewModelScope for long-lived operations. lifecycleScope is only for UI-bound tasks.
Directly calling viewModel.someFlow.collect { ... } inside lifecycleScope.launch continues collecting data even when the screen is not visible. This can lead to UI updates in the background and unnecessary overhead. Always use repeatOnLifecycle or flowWithLifecycle.
Although lifecycleScope is cancelled at DESTROYED, code after a suspend point may not execute on sudden cancellation. Do not rely on post-code execution after a suspend call unless you use NonCancellable.
launchWhenStarted and its counterparts do not cancel the coroutine, they only pause it. If the screen switches between foreground and background multiple times, the coroutine accumulates deferred calls. Switch to repeatOnLifecycle — this is the only correct way to synchronize with the Lifecycle.
Frequently Asked Questions
lifecycleScope is automatically cancelled when the LifecycleOwner is destroyed. GlobalScope lives for the entire duration of the application. A coroutine in lifecycleScope cannot update the UI after the component is destroyed, whereas in GlobalScope it can, leading to crashes. Always use lifecycleScope in the UI layer.
No, ViewModel is not a LifecycleOwner, so lifecycleScope is not available in it. ViewModel uses viewModelScope. If the code needs to run in both contexts, extract the logic into a use case or repository with suspend functions.
Each call to repeatOnLifecycle creates a new coroutine that launches the block when the specified Lifecycle state is reached. If repeatOnLifecycle is called twice for the same state, both blocks will run independently. Usually, one call in onViewCreated is sufficient.
You cannot directly change the lifecycleScope dispatcher — it uses Dispatchers.Main.immediate. Inside the coroutine block, you can switch to another dispatcher using withContext. For tests, use TestDispatcher with LifecycleOwner.
lifecycleScope is cancelled when the LifecycleOwner transitions to the DESTROYED state (after onDestroy). Simple lifecycleScope.launch calls are not cancelled in onPause or onStop. To pause when going to the background, use repeatOnLifecycle(STARTED) or repeatOnLifecycle(RESUMED).
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also