FireMonkey Save State

From RAD Studio
Jump to: navigation, search

Go Up to FireMonkey Applications Guide


The FireMonkey Save State feature allows you to save data describing the state of the application before it closes. The Save State data can be used to recover the state after the application is restarted.

How Devices Work Without the Save State Feature

On mobile devices, especially with Android platform, when an application is in the background, the operating system might decide to kill the process. This might be due to certain conditions such as low memory or simply because there are too many processes running. In this case, the information entered by the user does not remain in the application.

On Android, turning on the Don't keep activities option under Settings > Developer options makes the application restart when pressing the Home button and then getting back to the application, so the previously entered data is lost.

To solve this problem, Android provides a special storage called "SaveState", where the state of the application is stored and handed back when the application is restarted. The saving of this state is only "transient"; if you shut down the application manually or from the task manager and then restart it, the previous "transient" state will be lost.

After an application restarts, it might be convenient that the application remains in the same state it was before going to background.

Save State Feature in FireMonkey

The FireMonkey Save State feature allows you to save data with the state of the application before it closes. The Save State data can be used to recover the state after the application is restarted.

The Save State can include information such as the active tab, the text contained in edit controls, the checkbox selection, the calendar date, the current selected item, etc., along with user data.

The FireMonkey Save State feature implements a mechanism to handle both the Android transient "SaveState" mechanism and the persistent Save State by storing data on disk. On non-Android platforms, this feature emulates a transient Save State by saving files in temporary locations, and it stores the persistent state in a desired location.

The behavior of this feature on desktop platforms is similar to its behavior on mobile platform, that is, the Save State stores the application state. The storage is not guaranteed to be persistent; for example, if you delete the temporary file on Windows where this information is stored, the application restarts as a fresh startup and without getting back to the state in which it was before it was closed.

To handle the saving of this data, TForm has the OnSaveState event: any form data should be saved during this event so it can be recovered when the application restarts.

FireMonkey forms derived from TCommonCustomForm, such as TForm and TForm3D, have the SaveState property of type TFormSaveState, with the purpose of managing the Save State. The TFormSaveState class provides methods and properties to manage the Save State of the form, and it uses the IFMXSaveStateService service; that defines the functionality required for getting and setting the Save State on the target platform.

Memory Stream

The SaveState.Stream property is where the memory stream is stored as a TMemoryStream. It is possible to read and/or write to this property at any time, although:

  • Writing data to the Stream property should preferably occur during the OnSaveState event.
  • Reading data from the Stream property should preferably occur during the OnCreate event.

You can use helper classes, such as TBinaryReader and TBinaryWriter, with this stream as a parameter to assist saving information such as numeric and string values.

You can also access this feature globally, without restoring the TCommonCustomForm. You can request the IFMXSaveStateService service from the platform and use its methods to save and load the Save State data.

Storage Location and Name

The SaveState.StoragePath property allows specifying a "storage path" for the Save State:

  • If the StoragePath is empty (by default), the Save State is transient, of temporary nature, and it will either use facilities provided by the operating system (such as "SaveState" on Android) or a temporary location.

The data remains there if the activity is restarted by the operating system, but it is lost if the application is killed manually by the user in the case of mobile platforms. On other platforms, the data is saved to a temporary location and remains available until these temporary files are deleted (on Windows, for example, during Disk Cleanup).

  • If the StoragePath is not empty (it is set for example to TPath.GetHomePath), then the Save State is persistent and will be saved to disk at that location.

The Save State data remains untouched during execution, it stays this way and you can still recover information from it until you explicitly clear this data by using SaveState.Stream.Clear. If you clear the data using SaveState.Stream.Clear or calling IFMXSaveStateService.SetBlock with the block data set to nil, the data is removed from the transient storage or the file is deleted in the case of persistent storage.

We recommend that you set the SaveState.Name property to a desired name, or file name, if it is on disk, for both cases of transient and persistent states. If the Name property is left empty, the form creates a name, appending a combination of the application name, class name, and form name.

Adding Save State to Your Application

To illustrate the usage of the Save State feature:

  1. Create a new Multi-device application.
  2. Drop a TEdit on the form.
  3. Select the form, double-click the OnCreate event, and add the following code:
  4. Delphi:

    procedure TForm1.FormCreate(Sender: TObject);
    var
      R: TBinaryReader;
    begin
      if SaveState.Stream.Size > 0 then
      begin
        // Recover previously typed text in Edit1 control.
        R := TBinaryReader.Create(SaveState.Stream);
        try
          Edit1.Text := R.ReadString;
        finally
          R.Free;
        end;
      end;
    end;
    

    C++:

    void __fastcall TForm1::FormCreate(TObject *Sender)
    {
    	TBinaryReader *R;
    	if (SaveState->Stream->Size >0) {
    		// Recover previously typed text in Edit1 control.	
    		R = new TBinaryReader(SaveState->Stream, TEncoding::UTF8, false);
    		try {
    			Edit1->Text = R->ReadString();
    		} __finally {
    			R->Free();
    		}
    	}
    }
    
  5. Go back to the Form Designer, select the form, double-click the OnSaveState event, and add the following code:
  6. Delphi:

    procedure TForm1.FormSaveState(Sender: TObject);
    var
      W: TBinaryWriter;
    begin
      SaveState.Stream.Clear;
      // Current state is only saved when something was edited.
      // If nothing has changed, the state will be removed this way.
      if Edit1.Text.Length > 0 then
      begin
        // Save typed text in Edit1 control.
        W := TBinaryWriter.Create(SaveState.Stream);
        try
          W.Write(Edit1.Text);
        finally
          W.Free;
        end;
      end;
    end;
    

    C++:

    void __fastcall TForm1::FormSaveState(TObject *Sender)
    {
    	TBinaryWriter *W;
    	SaveState->Stream->Clear();
    	// Current state is only saved when something was edited.
    	// If nothing has changed, the state will be removed this way.
    	if (Edit1->Text.Length() >0) {
    		// Save typed text in Edit1 control.
    		W = new TBinaryWriter(SaveState->Stream);
    		try {
    			W->Write(Edit1->Text);
    		} __finally {
    			W->Free();
    		}
    	}
    }
    

    In the above code snippet, the save data is transient, and Edit1 continues to have its text even if the application is restarted. Since this Save State is transient, the state data will be deleted if the application is closed (manually shut down) in mobile devices.

  7. To make this save data persistent, add the following line at the beginning of the FormCreate event:
  8. Delphi:

    SaveState.StoragePath := TPath.GetHomePath;
    
    Note: Make sure that System.IOUtils is included in the uses clause.

    C++:

    SaveState->StoragePath = System::Ioutils::TPath::GetHomePath();
    
    Note: Make sure that #include <System.IOUtils.hpp> is included in the header file.

If you are running the application on the Windows Vista platform or later, a temporary file with the name ~ApplicationName_FM_ClassName_FormName.TMP (for example, ~SaveStateCodeSnippet_FM_TForm1_Form1.TMP) is saved in the following home path: C:\Users\<username>\AppData\Roaming.

See Also