hackbrew 1 Posted July 10, 2023 Currently, I'm working on an Android 13 FireMonkey mobile app (Delphi 11.3) with a TabControl with about four tabs. The app is grabbing JSON data from a URL and manually parses and loads the data into a FDMemTable. The app has very small data requirements (ie., single user, single table, and the JSON file would exchange only about 25 records per day containing about a dozen or so key/value pairs). Besides the memory table, I'm using an embedded database (SQLite) to store the data on device using LocalSQL features to implement database CRUD operations. My concern is persistence and restoring state if the app were to say crash/lose focus/device reset. Right now each time the app gains focus it starts from the beginning. For example, when running the app and then a call comes in on the device, my app loses its focus. When you return back to the app it starts as in the first time. I would like it if when the app restarts to remain in the same state it was before going to the background. I played around with the OnSaveState event on my main form, but it didn't behave the way I expected. For example, I send a mapping intent from my app to the default mapping app on the device for navigation. In this instance, the user temporarily leaves my app when the mapping app opens, and when closed, the user should return to my app. When I implement the OnSaveState event into my app (to read/write data to a stream) the user never returns to my app after closing the mapping app. But, if I remove the OnSaveState event, it does return back to my app, which is the desired behavior. How should persistence and restoring state be handled in an Android app? Share this post Link to post
programmerdelphi2k 237 Posted July 10, 2023 I think that is explained here not? read about "SaveState" usage on Android apps: https://docwiki.embarcadero.com/RADStudio/Alexandria/en/FireMonkey_Save_State Share this post Link to post
Der schöne Günther 316 Posted July 10, 2023 That does not sound like intended behaviour. Does that also happen if your implementation of the OnSaveState event is completely empty? Share this post Link to post
hackbrew 1 Posted July 10, 2023 (edited) @Der schöne Günther Well, I have minimal code in the OnSaveState event. procedure TMainForm.FormSaveState(Sender: TObject); var W: TBinaryWriter; begin SaveState.Stream.Clear; // Save app data to restore state later. W := TBinaryWriter.Create(SaveState.Stream); try ShowMessage('FormSaveState fired'); finally W.Free; end; end; In my FormCreate event, I have: SaveState.StoragePath := TPath.GetHomePath; // Required to make this save data persistent if SaveState.Stream.Size > 0 then begin R := TBinaryReader.Create(SaveState.Stream); try ShowMessage('R: ' + R.ReadString); finally R.Free; end; end; Is there something I'm missing because I'm never seeing the "FormSaveState fired" message displayed? Edited July 10, 2023 by hackbrew Additional info Share this post Link to post
Der schöne Günther 316 Posted July 10, 2023 (edited) Well, have you tried just having an empty implementation of TMainForm.FormSaveState(..)? I have not tried, but I would assume that when OnSaveSave gets called, you app has already been sent to the background and is about to be suspended within the next few milliseconds. Instead of actually saving something, your code example is now stuck on waiting for a confirmation for a non-visible dialogue. I would assume that the operating system either discards your handler or maybe entirely termiates your app because it appears stuck. Edited July 10, 2023 by Der schöne Günther Share this post Link to post
hackbrew 1 Posted July 10, 2023 @programmerdelphi2k Is the data wiped from the memory table and/or embedded database (SQLite) if the app were to say crash/lose focus/device reset? Share this post Link to post
hackbrew 1 Posted July 10, 2023 @Der schöne Günther After terminating the mapping app (Google or Waze), the focus will return to my app right where I left off, which is the desired outcome. Conversely, if the OnSaveState event is added to the MainForm and I exit the mapping app, I'm returned to the Home screen. Share this post Link to post
Der schöne Günther 316 Posted July 10, 2023 1 minute ago, hackbrew said: Conversely, if the OnSaveState event is added to the MainForm and I exit the mapping app, I'm returned to the Home screen. Thank you, that clarifies it. I don't think this is intended behaviour and probably a bug. Share this post Link to post
programmerdelphi2k 237 Posted July 10, 2023 (edited) 28 minutes ago, hackbrew said: Is the data wiped from the memory table and/or embedded database (SQLite) if the app were to say crash/lose focus/device reset? by default, the all "SaveState" is transitory (tmp), then, you can try save it permanently on disk using: xxSaveState;Name := 'xxxxx'; xxSaveState.StoragePath := 'xxxx'; like this: create your FOLDER before save it!!!! ALL components should exists on form as exists on SaveState file!!! // here Im not take care about "AV" ok? do it yourself... implementation {$R *.fmx} procedure TForm1.Button1Click(Sender: TObject); begin // you can use this on "OnCreate" from "form1"... or any place that you desire! // SaveState.Name := 'mySaves.TMP'; SaveState.StoragePath := 'd:\mytmpSaveStateDir'; // if SaveState.Stream.Size > 0 then begin for var i: integer := 0 to (ComponentCount - 1) do SaveState.Stream.ReadComponent(Components[i]); // reading... // ShowMessage('SaveState size on disk: ' + SaveState.Stream.Size.ToString); end else; end; procedure TForm1.FormSaveState(Sender: TObject); begin // 1º FormCloseQuery // 2º FormClose // 3º FormSaveState <---- // 4º FormDestroy // ... SaveState.Stream.Clear; SaveState.Name := 'mySaves.TMP'; // in disk, it will be [ ~project1_ + "your name to SaveState" ] SaveState.StoragePath := 'd:\mytmpSaveStateDir'; // for var i: integer := 0 to (ComponentCount - 1) do SaveState.Stream.WriteComponent(Components[i]); // saving all components on "Form1" // ShowMessage('FormSaveState fired'); end; end. Edited July 10, 2023 by programmerdelphi2k Share this post Link to post
programmerdelphi2k 237 Posted July 10, 2023 Another thing: as you use FDMemTable (I think), then, you can save your data on JSON/XML file using the properties for that: ResourceOptions ->PersistentFileName + Persistent=true then, when you re-open your table, the data should be there! Share this post Link to post
hackbrew 1 Posted July 10, 2023 @programmerdelphi2k Based on what you've shown here, it looks like the OnSaveState event appears to work for a VCL application, but from what I'm seeing there are conflicts with apps running on Android OS. I can successfully launch and send data to the mobile devices mapping app using an intent from my app, but it will not return back to my app after the termination of the mapping app when I use the OnSaveState event for some reason. When I click on a navigate button from my app, I leave my app and my default mapping app opens. Upon closing/terminating the mapping app, the focus will return to my app right where I left off, which is the desired outcome. If the OnSaveState event is added to the MainForm, when I exit the mapping app, I'm returned to the Home screen, not to my app which is a problem. Share this post Link to post
programmerdelphi2k 237 Posted July 10, 2023 (edited) 10 minutes ago, hackbrew said: If the OnSaveState event is added to the MainForm, when I exit the mapping app, I'm returned to the Home screen, not to my app which is a problem. and if you "assign" to current FORM active? unassign for any other... or be, only when you go to MAPs, you assign it, later unassign it! the app above is a FMX in MSWindows, not VCL!!!! then when app end, the SaveState is called as expected Edited July 10, 2023 by programmerdelphi2k Share this post Link to post
hackbrew 1 Posted July 10, 2023 @programmerdelphi2k Yeah sorry, meant MSWindows. Do you know if this is a known bug in Delphi 11.3 for Android apps when shelling out to another app via intent and expecting a return to the main app upon closing? Share this post Link to post
hackbrew 1 Posted July 10, 2023 58 minutes ago, programmerdelphi2k said: Another thing: as you use FDMemTable (I think), then, you can save your data on JSON/XML file using the properties for that: ResourceOptions ->PersistentFileName + Persistent=true then, when you re-open your table, the data should be there! @programmerdelphi2k Thanks, I didn't know those settings were there. On an app close or device power off/on, what happens to data in an on-device local SQLite table? Share this post Link to post
programmerdelphi2k 237 Posted July 10, 2023 13 minutes ago, hackbrew said: On an app close or device power off/on, what happens to data in an on-device local SQLite table? let's imagine a "regular case" = no critical-crash on the system! the app, would try receive the "message": hey app goes down! then, the form/app/OnClose...Destroy will be trigged this can be seen on MSWindows because it's more easy, on Android it's necessary more knowledge about how happens... I dont know! but you can do the tests... then, if you have the events "OnClose" (for example), you can to do some "quick"-task ... not save 1BILLION records of course! SQLite it's a DB one-file with many internal resource, then, you need read the documentation about your doubts. 20 minutes ago, hackbrew said: Do you know if this is a known bug in Delphi 11.3 for Android apps maybe not, maybe yes... maybe your code be the "bug"... needs more details/code and somebody that know how Android works behind the scenes... your code, how do you call the another app into your app? it's possible show the code? Share this post Link to post
Dave Nottage 557 Posted July 10, 2023 8 hours ago, hackbrew said: When I implement the OnSaveState event into my app (to read/write data to a stream) the user never returns to my app after closing the mapping app It's possible it's because your app crashed. When switching back to it, does it appear as if the app is restarting? I seem to recall OnSaveState being unreliable anyway due to it not being called at all if the app is "swipe closed" (I could be wrong). It might be more reliable to persist anything you need restored as soon as a change has occurred. 2 hours ago, hackbrew said: On an app close or device power off/on, what happens to data in an on-device local SQLite table? Using FireDAC, SQLite databases are by default in auto-commit mode, i.e. any changes should be saved immediately. Share this post Link to post
hackbrew 1 Posted July 10, 2023 3 minutes ago, Dave Nottage said: It's possible it's because your app crashed. When switching back to it, does it appear as if the app is restarting? I seem to recall OnSaveState being unreliable anyway due to it not being called at all if the app is "swipe closed" (I could be wrong). It might be more reliable to persist anything you need restored as soon as a change has occurred. Using FireDAC, SQLite databases are by default in auto-commit mode, i.e. any changes should be saved immediately. @Dave Nottage Yes, it appears, as best I can tell that the app is restarting. There's not too much data input on each tab or objects to deal with. So after not getting the OnSaveState to behave consitently, I've resorted to writing to a file to handle state management. Thanks! Share this post Link to post