Pocket Studio Academy
HomePart 44.8

Lifecycle, rotation and process death

Full course11 min read·4 questions

There are three different ways your state can vanish, and each one is stopped by a different tool. Learn which is which, and how to make process death happen on demand so you can actually test it.

The bug you cannot reproduce

You fixed rotation with a in Lesson 4.2. Here is a report you will get anyway:

"I was halfway through writing a note. I switched to the camera to check something, took a few photos, came back — and my note was blank."

You try it. It works fine. You try it ten times. It works fine every time.

It works because your phone had memory to spare. Theirs did not. While your app sat in the background, Android killed the entire process to keep the camera running smoothly. When the user came back, Android started your app again from scratch and restored what it had written down — and everything it had not written down was gone.

This is , and it is completely normal Android behaviour. It is not a crash. Nothing appears in the logs to say it happened.

Think of it like this

Imagine three different things happening to your desk.

Someone tidies the papers. Everything is still there, just neater. You carry on. That is .

The room is repainted. The desk is emptied onto a trolley, the room is redone, and the desk comes back. Anything you left loose on the surface is gone; anything you put in the drawer came back. That is a rotation.

The building is demolished and rebuilt overnight. The desk is new. The room is new. Everything is gone — except the few pages you posted to yourself before you left, which are waiting on the doormat. That is process death.

Three different disasters. Three different insurance policies. Using the wrong one is the whole of this lesson.

What survives what

Pin this table up somewhere.

rememberrememberSaveableViewModelRoom / DataStore
Recomposition
Rotation, dark mode, font size
Process death
User swipes the app away
Uninstall

Two rows deserve reading twice.

A ViewModel does not survive process death. It is an ordinary object in memory, and the memory is gone. Lots of people believe otherwise, because on a developer's phone — plenty of RAM, app in the foreground — it never comes up.

rememberSaveable survives process death but not a swipe-away. Swiping an app out of the recents view is the user saying "I am finished with this", and Android throws away the saved state too. That is correct: nobody wants a half-typed message from three weeks ago reappearing.

The Activity lifecycle, briefly

Android tells your where it is with six callbacks:

what Android calls, in orderkotlin
1onCreate()   // being built
2onStart()    // about to become visible
3onResume()   // in front, taking taps
4// ---- user opens another app ----
5onPause()    // no longer in front
6onStop()     // no longer visible
7onDestroy()  // going away

Compose hides nearly all of this. You almost never write these callbacks — the appears and disappears, and remember, LaunchedEffect and DisposableEffect handle the timing for you.

But two facts leak through and matter.

onStop is roughly where Android decides your app is a candidate for being killed. And saved state is written just before onStop, not at the moment of death — because when the process is killed, nothing gets to run at all.

Saving from a composable

rememberSaveable is remember plus a receipt:

ui/NoteEditorScreen.ktkotlin
1var title by rememberSaveable {
2  mutableStateOf("")
3}
4var body by rememberSaveable {
5  mutableStateOf("")
6}
7var loaded by rememberSaveable {
8  mutableStateOf(false)
9}

That is the real editor from Pocket Notes. Three saved values, and the third one is the clever one.

rememberSaveable only accepts what fits in a : numbers, strings, booleans, and types marked Parcelable. Hand it your own data class and you get an error naming exactly that — see the Error Doctor.

Saving from a ViewModel

A ViewModel can ask for a , which is a small map that does survive process death:

a ViewModel that survives a restartkotlin
1class EditorViewModel(
2  private val saved: SavedStateHandle
3) : ViewModel() {
4
5  var draft: String
6    get() = saved["draft"] ?: ""
7    set(value) {
8      saved["draft"] = value
9    }
10}

It goes into the same Bundle, so the same size limit applies. A draft is fine. A list of five hundred notes is not — and does not belong there anyway, because that is what the database is for.

The fix most apps actually use

Look at how Pocket Notes handles a half-written note:

ui/NoteEditorScreen.ktkotlin
1fun finish() {
2  vm.save(noteId, title, body)
3  onDone()
4}
5
6// The system back gesture must save too.
7BackHandler { finish() }

There is no Save button. Leaving the screen writes the note to — and once it is on disk, all three disasters stop mattering. rememberSaveable is only covering the gap between "you are typing" and "you left the screen".

That is the general shape of a good answer:

  • On disk for anything the user would be upset to lose.
  • rememberSaveable for what they are part-way through right now.
  • ViewModel for what is cheap to rebuild — a filtered list, a computed total.
  • remember for things that genuinely do not matter, like whether a menu is open.

Cleaning up on the way out

DisposableEffect is the tool for anything that must be undone when a composable leaves. Focus Flow uses it for the keep-screen-on setting:

ui/timer/TimerScreen.ktkotlin
1val view = LocalView.current
2DisposableEffect(keepOn) {
3  view.keepScreenOn = keepOn
4  onDispose { view.keepScreenOn = false }
5}

Set it on arrival; hand it back in onDispose. Without that, leaving the Timer tab would leave the phone's display pinned awake for as long as the app ran — the kind of bug users notice only as "this app eats my battery".

Try it in Pocket Studio

You will make process death happen on demand. Every Android developer should know this switch, and most learn it far too late.

  1. Open your phone's Settings, then About phone.
  2. Tap Build number seven times. It will tell you developer options are on.
  3. Go back, open System, then Developer options.
  4. Scroll to the Apps section and turn on Don't keep activities.
  5. Open Pocket Studio, run Pocket Notes, tap +, and type a title — but do not press Back.
  6. Press the phone's Home gesture, wait two seconds, then reopen the app from your recent apps.
  7. Your typed title is still there. That is rememberSaveable doing its job — Android destroyed the Activity completely and rebuilt it from the Bundle.
  8. Now open ui/NoteEditorScreen.kt, change both rememberSaveable calls to plain remember, and tap Run. Repeat steps 5 and 6. The title is gone.
  9. Put rememberSaveable back, tap Run, and turn Don't keep activities off again — leaving it on makes every app on your phone behave strangely.
Error Doctor5 common errors
java.lang.IllegalArgumentException: MutableState containing Note(id=1, title=Shopping) cannot be saved using the current SaveableStateRegistry. The default implementation only supports types which can be stored inside the Bundle. Please consider implementing a custom Saver for this class and pass it to rememberSaveable().
MeansYou gave rememberSaveable your own class. It can only save what fits in a Bundle: numbers, strings, booleans, and Parcelable types.
FixSave the small pieces instead — the id as a Long, the text as a String — and rebuild the object from them. That is almost always simpler than writing a custom Saver, and it keeps the Bundle small.
android.os.TransactionTooLargeException: data parcel size 1049148 bytes
MeansToo much was put into saved state. The Bundle crosses a process boundary and has a hard limit of around 1 MB — for the whole app, not per screen.
FixSaved state is for a scroll position and a half-typed sentence, not for data. Put lists in and keep only the id of what the user was looking at.
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
MeansSomething tried to change what is on screen after Android had already written down the state to restore — usually a result arriving from a background job after the app was backgrounded.
FixDo not act on results in a callback that can fire at any time. Put the value in a and let the screen read it whenever it next draws.
java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = com.nativeworks.pocketnotes.data.Note)
MeansA class was pushed into a Bundle by marking it Serializable and something inside it cannot be serialised — very often a lambda or a Context caught by accident.
FixDo not put whole objects in saved state. Store the id and reload. If you genuinely need to, use @Parcelize rather than Serializable — it is faster and fails at build time instead of at runtime.
Coming back to the app shows the first screen again, not the one I left
MeansNot an error at all — this is process death happening. Android restarted the app, and something on the way in is navigating to the start destination instead of letting the saved back stack restore.
FixCheck for a navigate() call running in a composable body rather than in an event, and make sure rememberNavController() is used. Turn on Don't keep activities to reproduce it in one second instead of waiting for a report.
Recap
  • Three ways to lose state, and they are not the same: , , and .
  • remember survives recomposition. survives rotation. and survive process death. Only disk survives everything.
  • Saved state lives in a with a hard size limit of about 1 MB for the whole app. Store ids, not data.
  • The real answer for anything valuable is to write it to or as it happens, the way the Pocket Notes editor does on the way out.
  • DisposableEffect undoes things when a composable leaves — the keep-screen-on flag is a real example.
  • Turn on Don't keep activities to reproduce process death instantly. Turn it off again afterwards.
  • Next: Part 4 is done, and you have every piece the three apps are built from. Part 5 builds them.