Your ViewModel Won't Save You: Designing for Android Process Death
How to use SavedStateHandle and the Single Source of Truth pattern to build "death-proof" apps that never lose user progress.
TL;DR:
- ViewModel survives rotations, but not process death.
- LMK (Low Memory Killer) will kill your background app to reclaim RAM.
- SavedStateHandle is your tool for restoring small UI states (IDs, flags).
- Room/DataStore is required for critical business data persistence.
- Test using
adb shell am kill <package>to see your app's true resilience.
The Ghost in the Machine
You’re in a banking app, halfway through a wire transfer. You switch to your email to copy an OTP. When you return, the app has reset to the login screen, and your progress is gone.
To the user, this is a “glitch.” To a seasoned Android developer, this is a failure to architect for Process Death. Android enforces strict process management regardless of available RAM. If you want to build professional software, you must stop assuming your app will stay alive in the background.
1. The Visual Mental Model: How State is Lost & Found
Understanding the timeline of a “Background Kill” is the first step to fixing it:
The Process Death Flow:
- User leaves app → App enters the background.
- Memory Pressure → System (LMK) kills the app process.
- User returns → Android recreates the Activity and ViewModel.
- The Gap → Standard variables in ViewModel are now
nullor reset. - The Rescue → SavedStateHandle restores UI state → Repository re-fetches data.
2. The Bouncer: How LMK Really Works
The Low Memory Killer Daemon (lmkd) is the system’s “bouncer.” Modern Android (10+) utilizes PSI (Pressure Stall Information) alongside OOM (Out Of Memory) Adjustment scores.
PSI allows the system to detect memory pressure before the device starts “thrashing.” If your app is in the background and consuming significant resources (like unoptimized bitmaps), it becomes the LMK’s primary target.
Pro Tip: Visibility is not a guarantee of survival. Process death can occur even while your app is visible under extreme system pressure — though it is far more common once the app is sent to the background.
3. ❌ Common Mistakes Developers Make
Before we look at the solution, are you committing these architectural “sins”?
- Storing API responses in ViewModel variables: These vanish instantly during process death.
- Assuming high-end devices are “safe”: Even a phone with 16GB of RAM will kill background processes to prioritize a 4K video recording or a heavy gaming session.
- Using Singletons as state holders: Process death wipes the entire memory heap, including your static Singletons.
- Saving large objects in the Bundle: This leads to the dreaded
TransactionTooLargeException.
4. The Solution: Resilience via SavedStateHandle
SavedStateHandle is backed by the system’s saved instance state (Bundle) mechanism. It allows your ViewModel to access the bundle data during recreation without needing to reference the Activity directly.
💻 Production-Grade Kotlin Implementation
Here is how to structure a Search feature using a Single Source of Truth (SSOT) pattern.
class SearchViewModel(
private val savedState: SavedStateHandle,
private val repository: ProductRepository // SSOT (Room/Network)
) : ViewModel() {
companion object {
private const val KEY_QUERY = "search_query"
}
// 1. Restore UI state (the "Where was I?")
// This survives process death and is observed by the UI
val searchQuery = savedState.getStateFlow(KEY_QUERY, "")
// 2. Reactively derive data (the "What was I looking at?")
// We re-fetch from the repository instead of saving the whole list
val searchResults: StateFlow<SearchStatus> = searchQuery
.flatMapLatest { query ->
if (query.isEmpty()) flowOf(SearchStatus.Empty)
else repository.searchProducts(query)
}
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), SearchStatus.Loading)
fun onQueryUpdate(newQuery: String) {
// Writing to SavedStateHandle persists it in the bundle
savedState[KEY_QUERY] = newQuery
}
}5. Testing the “Recreation” Flow
Don’t rely on the “Don’t keep activities” toggle in Developer Options. It only destroys the Activity, not the process. To simulate a real-world LMK event, use this ADB command while your app is in the background:
adb shell am kill <your.package.name>🙋 Frequently Asked Questions (FAQs)
Does this happen if the user swipes the app away?
Yes — but with a key difference. When the user swipes the app away from “Recents,” the process is killed and the saved state is discarded. The app starts completely fresh next time. For data that must survive a swipe-away (like a shopping cart), always use Room or DataStore.
Is rememberSaveable the same thing?
In Jetpack Compose, rememberSaveable uses the same underlying Bundle mechanism as SavedStateHandle. It’s perfect for UI-only state like scroll positions within a Composable.
How much data is “too much” for the Bundle?
As a rule of thumb, keep it well under the ~1MB Binder transaction limit (ideally <100KB). If you find yourself saving large objects, save an ID instead and re-fetch the data from your database during recreation.
🏁 Final Thoughts
Android memory management is aggressive by design. Process death isn’t a bug you should try to prevent; it’s a lifecycle event you must embrace. By separating your Transient UI State (SavedStateHandle) from your Persistent Business Data (Room), you ensure that your users never lose their place again.
If your app can’t survive process death, it isn’t production-ready.
💬 Join the Conversation
- Which famous app have you seen fail the process death test? (OTP screens are the biggest culprits!)
- How do you handle the “Loading” state when a process is recreated?
- Next Up: Deep Dive: SavedStateHandle vs. rememberSaveable — Which should you choose?
Stop assuming your app will stay alive. Build for recreation.
📘 Master Your Next Technical Interview
Since Java is the foundation of Android development, mastering DSA is essential. I highly recommend “Mastering Data Structures & Algorithms in Java”. It’s a focused roadmap covering 100+ coding challenges to help you ace your technical rounds.
- E-book (Best Value! 🚀): $1.99 on Google Play
- Kindle Edition: $3.49 on Amazon
- Also available in Paperback & Hardcover.

Comments
Post a Comment