Lifecycle, rotation and process death
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.
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.
remember | rememberSaveable | ViewModel | Room / 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:
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 awayCompose 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:
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:
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:
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.
rememberSaveablefor what they are part-way through right now.- ViewModel for what is cheap to rebuild — a filtered list, a computed total.
rememberfor 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:
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".
You will make process death happen on demand. Every Android developer should know this switch, and most learn it far too late.
- Open your phone's Settings, then About phone.
- Tap Build number seven times. It will tell you developer options are on.
- Go back, open System, then Developer options.
- Scroll to the Apps section and turn on Don't keep activities.
- Open Pocket Studio, run Pocket Notes, tap +, and type a title — but do not press Back.
- Press the phone's Home gesture, wait two seconds, then reopen the app from your recent apps.
- Your typed title is still there. That is
rememberSaveabledoing its job — Android destroyed the Activity completely and rebuilt it from the Bundle. - Now open
ui/NoteEditorScreen.kt, change bothrememberSaveablecalls to plainremember, and tap Run. Repeat steps 5 and 6. The title is gone. - Put
rememberSaveableback, tap Run, and turn Don't keep activities off again — leaving it on makes every app on your phone behave strangely.
rememberSaveable your own class. It can only save what fits in a Bundle: numbers, strings, booleans, and Parcelable types.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.Serializable and something inside it cannot be serialised — very often a lambda or a Context caught by accident.@Parcelize rather than Serializable — it is faster and fails at build time instead of at runtime.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.- Three ways to lose state, and they are not the same: , , and .
remembersurvives 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.
DisposableEffectundoes 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.