Is it wise to use OnSaveState in FMX?

Android’s lifecycle model is a mess. The OS freely kills your process whenever it needs RAM, and you—the app developer—are expected to deal with the fallout. Android shifts its memory problems onto the app layer, leaving you to rebuild your internal state because the OS felt like freeing 200 MB.

FireMonkey tries to give you a seatbelt for this chaotic ride: TForm.OnSaveState.
The idea is noble. The implementation is not.

What OnSaveState actually does

FMX exposes a GUI-level state persistence hook. When Android decides your app is disposable, it sends a state-saving request to the activity. FMX forwards that to your form via OnSaveState. You receive a TBinaryReader/TBinaryWriter-style blob (TMemoryStream under the hood) called SaveState.

You are supposed to dump enough data in there so that FMX can later reconstruct the UI and continue execution “as if nothing happened”.

In practice, this mechanism suffers from structural issues:

1. The OS can kill your app at any time — including during SaveState execution itself.
This is in the documentation and confirmed by developers in the DelphiPraxis threads. Sometimes the app is killed before SaveState even fires.

2. SaveState stores UI fragments, not your application model.
FMX restores controls, focus, selected text, combo-box indexes, and similar GUI details.
It does not know anything about your object graph, business logic, caches, back-end state, or anything stored exclusively in memory.

3. Restore timing is unpredictable.
Your app may get recreated hours later. Or immediately. Or not at all.
Your state blob may be discarded by the OS as “low priority”.

4. Manufacturers (Xiaomi, Oppo, Samsung…) cripple the lifecycle even further.
Some devices kill apps aggressively and restart them with minimal respect for the documented lifecycle. This thread show apps being killed even during camera usage (TakePhotoAction), and restored in an inconsistent state.

Bottom line: SaveState is not reliable. At best it lets the UI survive a soft process eviction. At worst it never runs, runs too late, corrupts data, or produces half-baked restores because your logic is not included.

The sane alternative: persist your data continuously

A much more robust strategy is to forget SaveState and treat your app as if it can die at any moment—because it can.

Whenever the user changes something meaningful, persist your internal object state.
Not the GUI—your actual application data: classes, lists, documents, session info, whatever defines the logical state of your program.

On startup, load from disk and rebuild the UI from your persistent model.
This way:

  • You don’t care if the OS kills your process.
  • You don’t depend on OnSaveState firing.
  • You don’t rely on FMX’s half-automatic GUI serialization.
  • Your restore logic is deterministic and fully under your control.

Implementation notes

Never overwrite the previous save file.
Your app can be killed while writing the file—this is a very real scenario on Android. If that happens and you overwrote your only file, you lose all state.

Use transactional persistence. For example:

  • Write to file.tmp.
  • Flush.
  • Rename to file.dat atomically.

Or keep an incremental filename. Many game engines use this model because crashes during save are common.

Binary serialization is fine and fast. JSON is slower but easier to debug. The important part: your logic survives, not just your UI surface.

When OnSaveState is still useful

Despite its shortcomings, SaveState isn’t completely useless. It helps with:

  • preserving temporary UI state (cursor position, panel size, scroll position);
  • avoiding jarring visual resets when the app is backgrounded and quickly resumed;
  • implementing quick-resume on platforms where lifecycle behavior is predictable (mainly iOS).

If you treat SaveState as a cosmetic enhancement rather than a core persistence mechanism, it can improve UX. But it should never be your source of truth.

Verdict

SaveState is a second-class citizen. It captures GUI state, not actual application state. It’s fragile, inconsistent across devices, and vulnerable to the OS killing your app before or during the save operation.

Save your logical state continuously. Treat process death as normal. Rebuild your UI from real data on launch. That’s the only approach that consistently survives Android’s memory-panic kills

 

 

Leave a Comment

Scroll to Top