The short answerWhile you are away, Android may kill the app, usually to free memory, without running a line of its code. When you come back, only what the app saved returns.
The film
Film coming soon
Leaving does not close the app
When you leave, the app's process, the app running in memory, does not end. It stays cached: kept for later.
When memory runs short, Android kills only as many processes as it needs. Cached apps are the most likely to go; apps on screen are the least likely, but not immune.
A video in a small window (picture-in-picture) is on screen: its app, rarely killed, is paused; the video plays on.
Who goes first when memory runs short
-
In frontleast likely
the app you are using
-
Also on screenunlikely
a video in a small window (picture-in-picture), the other half of a split screen
not immune -
Cachedmost likely
the apps you left (yours in amber); the longest unused goes first
The app is not told: not one line of its code runs, not even onDestroy, the last method Android calls on a screen.
Only what the app saved comes back
The app's memory dies with its process. Two things can bring your message back.
Its saved state: a small summary of the screen, which can hold the text you typed. Android keeps it in its own memory, outside the app, not on disk. Or a file, written to the phone's storage. Both survive Android's kill.
Swiping the app away, flicking its card off the recent apps, also ends the process. But you closed it, so Android drops the saved state too.
Where does the app keep your message?
| Where the app keeps your message | Android's kill | Your swipe |
|---|---|---|
| Only in memory | lost | lost |
| Saved state, in Android's memory | kept | lost |
| A file, in storage | kept | kept |
Memory is lost both times. The saved state survives only Android's kill. The file survives both.
Every build loses its memory
The usual test, turning the phone, is not a kill: the process stays. So do a JavaScript app's state and a ViewModel, the memory a native screen keeps across a turn.
Android's kill reaches every build:
| Build | After Android's kill |
|---|---|
| Java, Compose | back on its screen, with its saved state |
| Expo | starts over by default, with only what it stored |
| Flutter | |
| Ionic | |
| Web app (PWA) |
Any app can be made to survive a turn; no app can refuse the kill.
Back to your message
Usually the app is still cached, and your message is there. Sometimes Android killed it, and the app had kept your message only in memory.
The rule
Save the message to a file as the user types, and it survives the kill and the swipe.
Recap
- Leaving is not closing. Apps you left are the most likely to be killed.
- No last word. A killed app runs no code, not even onDestroy.
- Two kills. Android's kill keeps the saved state; your swipe keeps only the file.
What survives each kill Android's kill Your swipe · its card off the recent apps The app's memory lost lost Saved state · the screen's summary kept lost A file · in storage kept kept - Every build. Expo, Flutter, Ionic and a PWA start over by default after both kills.
With knowledge · in class · 20 minutes
Ask for a message that survives the kill
- Tell
- Check
- Test
-
Tell the assistant
First, a text box. None yet? Ask: “Add one text box to the first screen.”
This app runs on Android.This web app (PWA) runs in Chrome on Android. Once the user leaves, Android may kill it at any time without running any of its code.Save the draft to storage (SharedPreferences, written from a TextWatcherDataStoreAsyncStorageshared_preferencesCapacitor Preferences, not localStorageIndexedDB, with idb-keyval) as the user types,read it back when the screen opens,and tell me where each piece of state lives: memory, saved state or storage.In its replyPaste step 1 again, then check again.
-
Check what it wrote
The files decide, not the reply.
Look atWhereTick what you see
-
Saved as you type, at each key
Search
SharedPreferencesDataStoreAsyncStoragePreferencesidb-keyval(Ctrl+Shift+F)Ask: “Save the draft from the text box's change listener, at each key.”
-
-
Test both kills
Run the new version. Then, each time: type a draft, press Home.
Needs adb:
adb deviceslists your phone. Not yet? Chapter 02's Try it. No cable? Android's kill, by hand: Try it, step 2.as in chapter 02 Type it: Copy waits for it.-
Android's kill
adb shell "pidof com.<yourname>.<app>com.android.chrome && am kill com.<yourname>.<app>com.android.chrome"Then: Recents → reopen it
No PID? Wrong package, or the app is not open: nothing was killed. Still on Expo Go? Kill
host.exp.exponent, then open your project again.Test the installed app, not a tab: a Chrome tab may put typed text back.Ask: “Read the draft back from storage when the screen opens.”
-
Your swipe
Recents → swipe its card away → reopen it from its icon
In Expo Go, the icon opens Expo Go: open your project from there.
Ask: “Save the draft to storage as the user types, and read it back from storage when the screen opens.”Ask: “Read the draft back from storage when the screen opens.”
-
-
Debrief
Your draft survived both kills. Which test, run alone, would have fooled you, and why?
One answer
Android's kill: a Java
EditTextwith an id, or Compose'srememberSaveable, brings the draft back from saved state, even with no storage. Only your swipe proves storage. Try it, just below, shows this with Contacts.Neither, in your build: it keeps no saved state by default, so each kill alone proves storage. In Java, Android's kill alone would fool you: anEditTextwith an id gets its text back from saved state.
Go deeper Every build, side by side 1 min
| Build | Store (search for it) | Written from | Package for am kill |
|---|---|---|---|
| Java | SharedPreferences | afterTextChanged, a TextWatcher | your applicationId |
| Compose | DataStore | onValueChange | your applicationId |
| Expo | AsyncStorage | onChangeText | your android.package; host.exp.exponent in Expo Go |
| Flutter | shared_preferences (SharedPreferences) | onChanged | your applicationId |
| Ionic | Capacitor Preferences, not localStorage | onIonInput | your applicationId |
| PWA | IndexedDB (idb-keyval) | onIonInput | com.android.chrome: test it installed |
Try it on your phone · 8 minutes, no cable
See a cached app, Android's kill and your swipe
Before you start: Developer options must be on (Settings → About phone → tap Build number seven times; on a Samsung, under Software information). The full setup, with adb, is in the Try it of What happens when you tap an app's icon?
-
Cached, not closed. Open Chrome and press Home. Then go to Developer options → Running services → ⋮ → Show cached processes.
You should see Chrome, marked “Cached background process”.
This shows Leaving does not close an app.
-
Android's kill, by hand. Set Developer options → Background process limit → No background processes.
If you restarted the phone less than 10 minutes ago, wait: the limit starts 10 minutes after the first unlock.
In Contacts, start a new contact and type a first name. Do not save it.
Press Home, then open two other apps.
Why two: the app you just left is spared a little longer.
Look at the cached processes (step 1), then go back to Contacts from the recent apps.
You should see No Contacts among the cached processes; back in Contacts, the name, still there.
What happened Android killed Contacts; its saved state brought the name back.
-
Your swipe. Press Home, swipe Contacts away in the recent apps, then open it.
You should see The contact list; the name you typed is gone.
This shows Your swipe drops the saved state.
Another Contacts app may keep the name: then it stored the draft itself.
Put it back: Developer options → Background process limit → Standard limit.
Quick check
An assistant's Java screen keeps the draft in a String field of its Activity (the screen's class), and writes it to a file in onDestroy. Android kills the app while the user is away. Is the file written?
See the answer
At a kill, no code runs, so the save planned in onDestroy never happens, and the draft dies with the process. Save as the user types.
You left your messages app an hour ago and have opened five apps since. A video plays in a small window over the game in front. Memory runs short. Which of the three is the most likely to be killed?
See the answer
Cached apps go first, the longest unused first. Apps on screen, the game and the small window, are the least likely, but not immune.
The draft is kept only in the saved state (in code, onSaveInstanceState or SavedStateHandle). It survives Android's kill (am kill), but not a swipe from the recent apps. Why?
See the answer
am kill is Android's own kind of kill, so the saved state survives it. A swipe says the user is done: Android drops the saved state on purpose. Only storage survives a swipe.
Your app passes the rotation test: turn the phone, and the draft stays. Is it safe from Android's kill?
See the answer
A turn keeps the process; the kill ends it. A draft in a ViewModel or a JavaScript variable survives only the turn; in saved state or a file, it survives both. Run the kill test.
Sources
- Android Developers: Processes and app lifecycle, cached processes, killed as needed; Memory allocation among processes, background apps first, the previous app after them; Add videos using picture-in-picture, a paused activity that keeps playing; Activity.onDestroy, skipped when the process is killed; Save UI states, saved state in memory outside the process, and what survives each kill; State and Jetpack Compose,
rememberandrememberSaveable; Handle configuration changes, the turn; Configure on-device developer options, the background process limit. - AOSP: Low memory killer daemon, the least essential processes killed first, only to keep the system at acceptable levels.
- The stores: SharedPreferences, DataStore, Expo AsyncStorage, Flutter shared_preferences, Capacitor: Storage, why Preferences rather than localStorage, and IndexedDB; Page Lifecycle API, why
beforeunloadis not a save point.