Activity Lifecycle Explained
ANDROID › Lifecycle
The Activity lifecycle is six callbacks arranged as three mirrored pairs. Climbing to the foreground: onCreate, onStart, onResume. Descending back down: onPause, onStop, onDestroy. A seventh callback, onRestart, only fires when a stopped but not destroyed activity comes back to the foreground, it sits right before onStart on the way up.
override fun onCreate(savedInstanceState: Bundle?) { } // one-time setup
override fun onStart() { } // becoming visible
override fun onResume() { } // gaining focus, now interactive
override fun onPause() { } // losing focus
override fun onStop() { } // no longer visible
override fun onDestroy() { } // torn down
Interviewers rarely just want the six names recited back. What they are actually probing is which subset of these callbacks runs for a given trigger: rotation, Back, Home, launching another activity, multi-window, process death. That subset-per-trigger question is what the rest of this lesson works through.
Cold start and returning to a stopped activity look similar from the outside, both end up Resumed, but they skip different callbacks. A cold start runs onCreate with a null Bundle, then onStart, then onResume. Returning to an activity that was merely stopped, never destroyed, for example reopening it from Recents, skips onCreate completely and starts at onRestart instead.
// User backgrounds via Home; the process is never killed.
// User reopens the same instance from Recents.
override fun onRestart() { Log.d(TAG, "onRestart") }
override fun onStart() { Log.d(TAG, "onStart") }
override fun onResume() { Log.d(TAG, "onResume") }
Because the instance was never destroyed, there is no saved state to restore either. The fields already hold whatever they held when the activity stopped, nothing was torn down.
Home and Back both take the activity off screen, but they mean very different things to the system. Pressing Home runs onPause then onStop, and then leaves the instance alone. It is not destroyed, it just becomes something the system could reclaim later if it needs the memory. Because that reclaim is only a maybe, the system also gives it a chance to save state along the way, in case the process does get killed before the user comes back.
Back, or calling finish(), also runs onPause then onStop, but this time onDestroy follows immediately after. The user made a deliberate, final choice to leave, there is no future recreation to prepare for, so no state gets saved at all.
// Home: pause, stop, instance kept alive, state saved just in case
override fun onPause() { super.onPause() }
override fun onStop() { super.onStop() } // instance survives, may be reclaimed later
// Back / finish(): pause, stop, then destroy right away
override fun onPause() { super.onPause() }
override fun onStop() { super.onStop() }
override fun onDestroy() { super.onDestroy() } // nothing left to save for
Same two callbacks up front either way. What happens after them, and whether anything gets saved, is what actually differs.
Which callback should own which work? onPause must return fast, the next activity cannot fully resume, and its window cannot draw, until your onPause finishes. Stop animations and release anything the incoming UI needs immediately, but do not do blocking IO here.
onStop is where the activity has become fully invisible, it is the right place for heavier cleanup: closing cursors, unregistering expensive listeners, or a database write. It is still not a guarantee of final teardown though, the OS may reclaim the whole process shortly after onStop without ever calling onDestroy.
override fun onPause() {
super.onPause()
player.pause() // cheap, must not block the incoming activity
}
override fun onStop() {
super.onStop()
repository.flushToDisk() // heavier, safe once fully hidden
}
onSaveInstanceState and its counterpart onRestoreInstanceState only run when the system might destroy the activity and recreate it later: a configuration change or process death. They do not run on Back or finish(), as covered earlier, there is nothing to restore into because the user chose to leave for good.
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString("query", currentQuery)
}
On the way back in, that Bundle shows up as the savedInstanceState parameter to onCreate, or you can read it in onRestoreInstanceState, which runs after onStart. A null Bundle in onCreate means either a genuine first launch, or a return to an activity that was stopped but never destroyed, since that path skips onCreate entirely. A non-null Bundle means the opposite: this instance is being recreated after a configuration change or after process death.
One ordering detail interviewers check: for apps targeting API 28, Android P, or higher, onSaveInstanceState is guaranteed to run after onStop. Below that target it ran before onStop, with no firm ordering against onPause.
There is one documented exception worth memorizing exactly: calling finish() inside onCreate(). Because the activity never actually became visible, the system skips onStart, onResume, onPause, and onStop entirely and jumps straight to onDestroy.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (!isUserEligible()) finish() // decided immediately not to show this screen
}
This matters for anything that assumes the pairs are symmetric. Code that only cleans up in onStop, on the assumption that onStart always ran first, will silently never fire here.
Launching another activity interleaves two lifecycles. When Activity A starts a full-screen, opaque Activity B, A pauses first, then B fully spins up, and only once B is completely visible does A stop, not before.
// A starts full-screen, opaque Activity B:
// A.onPause -> B.onCreate -> B.onStart -> B.onResume -> A.onStop
The key detail: A only reaches onStop once B is fully drawn and covering it.
If B uses a translucent, dialog-style theme instead, A stays partly visible behind it. In that case A still loses focus and gets onPause, but it never reaches onStop, because onStop is reserved for genuine invisibility and A never actually disappeared from the screen. A just sits at Paused for as long as B is on top.
Rotation pulls together everything so far. It's a configuration change, so the existing instance runs the full teardown: onPause, onStop, onSaveInstanceState (guaranteed after onStop on API 28+), then onDestroy. A fresh instance is created right after, and this time onCreate receives a populated Bundle, followed by onStart and onResume.
// Outgoing instance:
override fun onSaveInstanceState(out: Bundle) {
super.onSaveInstanceState(out)
out.putString("key", value) // saved after onStop, before onDestroy
}
override fun onDestroy() { super.onDestroy() } // last callback on the old instance
// New instance:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
value = savedInstanceState?.getString("key") ?: defaultValue
}
Process death is the same shape, with a gap in the middle where nothing is running at all. The OS killed the process outright to reclaim memory, then rebuilds a fresh instance later from the same saved Bundle when the user comes back. Done properly, the user can't tell the difference between that and a live rotation, the screen just looks like it never left.
Multi-window changes the rules. Two activities can be visible side by side, but only one holds focus. The unfocused one is Paused, not Stopped, even though it's still fully on screen. That's exactly why UI teardown belongs in onStop, tearing it down in onPause would blank a window the user can still see.
For exclusive singleton hardware like the camera, onPause or onResume are not precise enough either, several activities can be Resumed at once in multi-window. onTopResumedActivityChanged(Boolean), added in API 29, exists specifically to say which one is the single top-focused activity that should hold exclusive resources.
override fun onTopResumedActivityChanged(isTopResumedActivity: Boolean) {
if (isTopResumedActivity) camera = openCamera() else { camera?.close(); camera = null }
}
Launch modes change which of these callbacks even happen. By default, starting an activity that's already on the stack creates a brand new instance on top of it, with a full onCreate. With launchMode="singleTop", or the equivalent FLAG_ACTIVITY_SINGLE_TOP, if the target is already sitting at the top of the stack, the system does not create a new instance at all. No onCreate runs.
Instead, the existing top instance receives the new Intent through onNewIntent(), typically wrapped as onPause, then onNewIntent, then onResume.
// AndroidManifest.xml: android:launchMode="singleTop"
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent) // required on API 27+
setIntent(intent) // so getIntent() returns the latest intent
handleDeepLink(intent)
}
Forgetting setIntent(intent) is a common bug, without it, a later call to getIntent() still returns the original intent the activity was first created with, not the one that just arrived.
Inside onDestroy(), how do you tell a genuine finish apart from a transient destroy-to-recreate, like rotation? Call isFinishing(). true means the activity is really going away, false means it's only being torn down so a fresh instance can be rebuilt.
override fun onDestroy() {
super.onDestroy()
if (isFinishing) clearPersistentCache() // real finish: Back, finish(), task cleared
// else: config-change recreation, a new instance follows shortly
}
That distinction is exactly why ViewModel behaves the way it does. A ViewModel's store is retained across a configuration change, so it survives the false case untouched, its fields keep their values right through a rotation. It does not survive process death though. If the OS kills the process outright and later rebuilds it, a brand new ViewModel is constructed, and only whatever was written into a SavedStateHandle comes back, backed by the same kind of Bundle as onSaveInstanceState. Any plain in-memory field on the old ViewModel is simply gone.
One practical place all of this shows up in real code is collecting a Flow safely. The standard pattern is lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { flow.collect { ... } } }.
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect { state -> render(state) }
}
}
That block starts running the moment the lifecycle reaches STARTED, which is onStart, and gets cancelled the moment it drops below STARTED, which is onStop. Because STARTED, not RESUMED, is the threshold, collection keeps running even in the Paused-but-visible multi-window case covered earlier, the activity hasn't actually left the screen. Every time the lifecycle climbs back to STARTED, whether through a normal onStart or through onRestart then onStart, the block restarts automatically. There is no manual start or stop wiring to get wrong.
Every scenario in this lesson is the same six callbacks, plus onRestart, run in a different subset. Cold start: onCreate, onStart, onResume. Returning from a stopped instance: onRestart, onStart, onResume. Home: onPause, onStop, instance kept alive, state saved just in case. Back or finish(): onPause, onStop, onDestroy, nothing saved. Rotation and process death: the same teardown as Back, but with onSaveInstanceState added before onDestroy, followed by a fresh onCreate carrying the saved Bundle.
In an interview, that's the shape to reach for: name the pairs, then say which subset a given trigger produces and why. Call out the exceptions that trip people up: finish() inside onCreate skipping straight to onDestroy, a translucent activity on top leaving the one underneath at Paused instead of Stopped, and singleTop swapping onCreate for onNewIntent. And tie work to the right callback: cheap, blocking-sensitive work in onPause, heavier cleanup in onStop, anything that must survive a config change in onSaveInstanceState or a SavedStateHandle-backed ViewModel property, since a plain ViewModel field only survives rotation, not a process death. That combination, the exact ordering plus the reasoning behind it, is what separates someone who memorized six names from someone who understands the activity lifecycle.