Mehdi Tmimi Mobile

Mobile Development Chapter 03 · Android · 2 of 2

Why does your message sometimes vanish when you come back to an app?

You type a message, switch to another app, then come back. Usually your message is still there. Sometimes it is gone, and you never closed the app.

Leaving does not close the app. Android may end it later, without a word.

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.

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

Memory enough: Android stops
  1. In frontleast likely

    the app you are using

  2. Also on screenunlikely

    a video in a small window (picture-in-picture), the other half of a split screen

    not immune
  3. Cachedmost likely

    the apps you left (yours in amber); the longest unused goes first

Simplified: Android's ranking has a few more levels.

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?

What survives Android's kill and your swipe
Where the app keeps your messageAndroid's killYour swipe
Only in memorylostlost
Saved state, in Android's memorykeptlost
A file, in storagekeptkept
Force stop in Settings, or restarting the phone, drops the saved state too, like your swipe.

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:

After Android's kill, build by build
BuildAfter Android's kill
Java, Composeback on its screen, with its saved state
Expostarts 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

  1. Leaving is not closing. Apps you left are the most likely to be killed.
  2. No last word. A killed app runs no code, not even onDestroy.
  3. Two kills. Android's kill keeps the saved state; your swipe keeps only the file.
    What survives each kill
    Android's killYour swipe · its card off the recent apps
    The app's memorylostlost
    Saved state · the screen's summarykeptlost
    A file · in storagekeptkept
  4. 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

I build with
  1. 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 reply

    Paste step 1 again, then check again.

  2. Check what it wrote

    The files decide, not the reply.

    • 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.”

  3. Test both kills

    Run the new version. Then, each time: type a draft, press Home.

    Needs adb: adb devices lists your phone. Not yet? Chapter 02's Try it. No cable? Android's kill, by hand: Try it, step 2.

    • 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.”

  4. Debrief

    Your draft survived both kills. Which test, run alone, would have fooled you, and why?

    One answer

    Android's kill: a Java EditText with an id, or Compose's rememberSaveable, 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: an EditText with an id gets its text back from saved state.

Go deeper Every build, side by side 1 min
Where each build stores the draft, and which package am kill needs
BuildStore (search for it)Written fromPackage for am kill
JavaSharedPreferencesafterTextChanged, a TextWatcheryour applicationId
ComposeDataStoreonValueChangeyour applicationId
ExpoAsyncStorageonChangeTextyour android.package; host.exp.exponent in Expo Go
Fluttershared_preferences (SharedPreferences)onChangedyour applicationId
IonicCapacitor Preferences, not localStorageonIonInputyour applicationId
PWAIndexedDB (idb-keyval)onIonInputcom.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?

  1. 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.

  2. 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.

  3. 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

← All chapters