100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Kotlin

App Lifecycle and Process Death

How Android activities and processes move through creation, backgrounding, and destruction, and why saving state correctly is essential to survive system-initiated process death.

Testing & ProductionIntermediate10 min readJul 8, 2026
Analogies

App Lifecycle and Process Death

Every Android app runs inside a process that the operating system can create, pause, or kill without warning. The Activity lifecycle (onCreate, onStart, onResume, onPause, onStop, onDestroy) tells you when your UI is visible and interactive, but it does not tell the whole story: the OS can terminate your entire process while it is in the background to reclaim memory for other apps, and your app must be able to reconstruct its UI state as if nothing happened when the user returns. This is called process death, and it is one of the most commonly mishandled aspects of Android development because it rarely happens during casual manual testing but happens constantly on real, memory-constrained devices.

🏏

Cricket analogy: A batsman's innings can look continuous, but the umpire's over-count doesn't reveal that the groundstaff could halt play for rain at any moment, wiping the pitch conditions the batsman had adjusted to — just as the OS can kill your process mid-background without the Activity lifecycle warning you.

Configuration Changes vs. Process Death

It is important to distinguish two very different events that both destroy and recreate an Activity. A configuration change, such as a screen rotation, a theme toggle, or a font-scale change, destroys and immediately recreates the same Activity instance's UI within the same still-running process; in-memory state kept in a ViewModel survives automatically because ViewModel instances are retained across configuration changes by the Android framework. Process death, by contrast, is the OS killing the entire process while the app is backgrounded; when the user navigates back, Android creates a brand-new process and a brand-new ViewModel, so anything that lived only in memory is gone. Only persisted state — written to a Bundle via onSaveInstanceState, to SavedStateHandle, or to disk — survives process death.

🏏

Cricket analogy: A rain delay that pauses and resumes the same match (same players, same scoreboard) is like a configuration change — the ViewModel-equivalent scoreboard persists; but if the whole tournament were abandoned and restarted next season with new squads, that's process death — only recorded stats (persisted state) carry over.

SavedStateHandle in ViewModel

The modern, Compose-friendly way to survive process death is SavedStateHandle, which is automatically injected into a ViewModel by the ViewModel-ktx / Hilt integration. Values placed into SavedStateHandle are serialized into the same Bundle mechanism used by onSaveInstanceState, so they are restored even after the OS has killed and recreated the process. Because SavedStateHandle only supports Bundle-compatible types (primitives, Parcelable, Serializable), it should be reserved for small, essential UI state — such as the current tab index, a search query, or a scroll position — not entire domain objects, which belong in a repository or database instead.

🏏

Cricket analogy: A scorer jotting the current over, batsman's score, and required run rate onto a small card before leaving the box — not the entire ball-by-ball commentary — mirrors SavedStateHandle: save only the essential small facts (like tab index or scroll position), not the whole match archive.

kotlin
class SearchViewModel(
    private val savedStateHandle: SavedStateHandle,
    private val repository: SearchRepository
) : ViewModel() {

    // Survives configuration changes AND process death.
    val query: StateFlow<String> = savedStateHandle.getStateFlow("query", "")

    val results: StateFlow<List<SearchResult>> = query
        .debounce(300)
        .filter { it.length >= 2 }
        .flatMapLatest { repository.search(it) }
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5_000),
            initialValue = emptyList()
        )

    fun onQueryChanged(newQuery: String) {
        savedStateHandle["query"] = newQuery
    }
}

You can simulate process death without waiting for the OS to reclaim memory. On a device or emulator with Developer Options enabled, turn on "Don't keep activities," then background your app — Android destroys the process the instant you leave it, letting you test restoration behavior on every single navigation.

A common mistake is treating onDestroy() as a reliable place to save critical data. onDestroy() is not guaranteed to be called before process death — the OS can kill a background process without invoking any further lifecycle callbacks at all. State that must survive should be saved proactively (e.g., in onPause() via onSaveInstanceState, or continuously via SavedStateHandle/StateFlow), not deferred to onDestroy().

Beyond individual screens, Android ranks processes by importance to decide which to kill first when memory is low: a foreground process hosting a visible, actively interacting Activity has the highest priority and is almost never killed; a visible process (partially obscured, e.g. behind a dialog) is next; a background/cached process, whose Activities have all called onStop(), is the most likely to be killed, and can be terminated with no further callbacks. Long-running work that must survive backgrounding — network sync, uploads — should not rely on the Activity or process staying alive at all, and should instead be scheduled through WorkManager, which is independent of the hosting Activity's lifecycle.

🏏

Cricket analogy: The team currently batting on the field (foreground) is protected and never substituted mid-over; a team warming up in the dugout (visible) is next in line; a team that's already finished its innings and gone to the pavilion (background/cached) is most likely to be released from the squad first.

  • Configuration changes recreate the Activity within the same process; ViewModel state survives automatically.
  • Process death kills the entire process; only Bundle-based or disk-persisted state survives.
  • SavedStateHandle lets a ViewModel expose state as a StateFlow that automatically persists across process death.
  • Test process death explicitly using the 'Don't keep activities' developer option — never assume it just because manual testing looked fine.
  • onDestroy() is not a reliable place to persist state; save proactively, not reactively.
  • Background work that must outlive the process (uploads, sync) belongs in WorkManager, not in Activity or ViewModel scope.

Practice what you learned

Was this page helpful?

Topics covered

#Kotlin#AndroidWithJetpackComposeStudyNotes#MobileDevelopment#AppLifecycleAndProcessDeath#App#Lifecycle#Process#Death#StudyNotes#SkillVeris