I am currently developing an RPG game for Android devices and have just implemented a custom method of serialisation I use for saving the player's progress. The saving process can take up to half a second which is enough time for a crash to occur (either caused by the device (i.e. low battery power off), the user (killing the app) or a poorly written firmware/ROM (kernel panics) etc).
While saving the player's data, the old player data is overwritten. This means if a crash was to occur, and if the saving process were to be cancelled/interrupted as a result, the player's data would be lost. This is obviously not ideal and in the future, the game will be saving a lot more data and the save time will be much longer. This will increase the chance of a crash occurring during the save process.
I cannot reduce the save time as I am writing the minimal data the game requires to be able to resume after the app has been restarted.
What foolproof measures, if any, can I take to prevent such data corruption/loss from happening?
You can save your data in a temporary set of files and moving/renaming them when the process is complete, then deleting the previous save files.
If you're not confident with the renaming process, you can add these constraints :
ensure that data is consistent with a checksum
always try to resume from the last consistent saved state, depending on a rule of your own (name of the file, ...)
Another idea would be to cut into pieces your data in order to isolate state that do not change.
If save time is really long, you can try to use remaining CPU time during the game to pre-save parts of the current state that won't probably change (using a lower priority Thread, for instance).
You can save data to a SQLiteDatabase. If changes to the save data fails or is interrupted, the database will automatically be rolled back to a previous known state.
For additional security if you need to perform multiple updates atomically, put all your changes into a transaction. If any of the changes fail, the entire transaction will be rolled back to the pre-transaction state.
For more information about using SQLite, see documentation here. For easier manipulation of your save data in the event you want to share it with other apps or sync it to a backup server, consider interacting with your data via a ContentProvider.
Related
I got some issue when I developing an App.
After I minimized the app or turn the screen off, and open lot of other apps or reopen the phone after a long time.
When I restart my app, it try to resume and keep showing the same page(with fragment).
But the data I need was already been destroyed so it will be null.
The data is an object array, I know maybe I can store them in db.
But due to the data will update every time user click something.
So I don't want to save it into data base, I guess that means lot of storage I/O witch is not necessary.
I'm wondering if there is any solution to restart the hole app when things is destroyed?
Or the only way to make it happen is I handle the null array and do the reload myself?
I don't really want to do that cause I guess that will bring me many unexpected issues cause the data is related with many pages.
Too many situations I have to consider when do switching pages.
Are there any advice?
But the data I need was already been destroyed so it will be null
That is because your process was terminated and you did not save your state.
But due to the data will update every time user click something
Or, you could fork a thread to save the data as part of your onPause() or onStop() methods. There are many possibilities between "never save" and "save on every click".
So I don't want to save it into data base, I guess that means lot of storage I/O witch is not necessary
If you want the data to be there 30+ minutes after the user left the app, your choices are to save the data locally (file, database, SharedPreferences) or save the data on the Internet somewhere.
For small amounts of data over shorter time periods, you could put the data in the Bundle supplied to onSaveInstanceState() and then pull the data out of the Bundle again later (e.g., in onRestoreInstanceState() of your activity). You already should be doing this to handle screen rotations and other configuration changes.
I'm wondering if there is any solution to restart the hole app when things is destroyed?
You are welcome to add android:clearTaskOnLaunch="true" to your launcher activity, to indicate that you always want to start over from scratch whenever the user leaves your app and tries to come back to it. Users will not appreciate this, as this means that they will lose their state even for being out of your app briefly (e.g., a quick reply to a text message). This attribute does not terminate your process, but it will force the user back to the launcher activity and will eliminate any other activities that had been in your app previously.
Or the only way to make it happen is I handle the null array and do the reload myself?
That is what developers normally do, yes.
It seems that there is a large amount of information about saving Activity state, but I have been unable to locate much on finding Application state.
I am looking for some design ideas to solve a problem I have run into. I am developing a game that has a fairly large data model (1-2 MBytes). This model exists outside of any Activity, in fact there are numerous activities that all interact with it. These activities are transient, coming and going all the time.
I currently keep a pointer to the data model in my application and all of the activities access the data model through it. I need to save that data model in the event that my application is being killed, but it is far too slow to save it every time an activity hits onPause, which happens very frequently as activities come and go.
What I need is a way to determine that my application (and along with it my data model) are being destroyed. I have searched extensively for this method or callback and have come up empty.
I would appreciate any suggestions.
I have been unable to locate much on finding Application state.
That's because there is no "Application state" in Android, any more than there is in a Web app.
but it is far too slow to save it every time an activity hits onPause
While your entire data model may be "1-2 MBytes", but the amount of data that changes is going to be a small subset of that, for any given change. Use a background thread and only modify the data that has changed.
which happens very frequently as activities come and go
It sounds like perhaps you have too many activities.
What I need is a way to determine that my application (and along with it my data model) are being destroyed
That is not possible. You will never find out that you are being destroyed. Android can and will terminate your process without warning, either at user request (e.g., Force Close, task killer) or for OS reasons (e.g., need the RAM to handle an incoming phone call).
You are welcome to use onUserLeaveHint(), which is called in a number of cases when you entire app loses the foreground, but I certainly would not count on that for something as important as persisting a data model.
Attempting to decide (for my application) what to save in onPause() and what to save in onSaveInstanceState(), I combed the entire SO for hints and clear guidelines.
If I understand correctly, onSaveInstanceState() is best for saving "runtime changes" or "current state" (whatever that means), while onPause() is best for saving "persistent state" (whatever that means).
I am still having difficulty deciding what in my application constitutes "persistent state" vs. "current state". For example, while user preferences are clearly persistent, do I need to save them in onPause() when they are always saved automatically by the Android UI framework when the user changes them?
Do class data members need to be saved in onSaveInstanceState()? Do I need to do that for every class in my application?
I am confused.
Can you bring real-world examples of what must be saved in onPause() and what must be saved in onSaveInstanceState()? Except for device configuration changes, that is.
--
Some new insights, after my question has been answered:
onSaveInstanceState's Bundle is not written to anything, and it's not persistent in any way.
onSaveInstanceState's Bundle data will only be held in memory until the application is closed.
You do not need to store user preferences in onPause because as you say, the framework does that for you.
To distinguish between persistent data vs state information, think of a text editor application.
Persistent data
Let's say the user has typed a couple words and then exits the app. The user didn't explicitly tell us to save that data to a file, but it sure would be nice to store that data away for when they come back. This is persistent data and you want to store it away in onPause().
State data
Similarly, say you have 2 tabs and a variable that tracks which tab is currently selected. This is state data that you'd store in onSaveInstanceState().
Gray matter
Finally imagine you have a class in the editor that keeps track of the number of characters and number of lines in the editor. This is state data, you could store it in onSaveInstanceState() or you can throw it away and simply recalculate it when you start up again. Whether you throw it away might depend on how long it takes to calculate, for instance if you could prevent a network request by storing data, do so.
Further thoughts
By playing with your app it should be obvious if there's an area where you failed to squirrel the right data away. Be sure to do things like hit the home button and then close out your app from the device manager. This will let you hit the corner cases where your app is shut down rather than just paused.
If your UI state is consistent across lifecycle events and your user data remains, good job.
Edit based on comment
I think there are 2 pieces of criteria here to determine when/what to save.
The first is quite subjective - Do you want to save data at all? There's truly nothing forcing you to save state or data. Will saving this information make for a better user experience? If you are writing an email and trying to copy/paste text from another app, losing your half typed email every time the app gets closed would be frustrating.
The second piece, determining what to save depends on whether you can reconstruct your UI state based on the data that you have. For instance, if you have saved text data then that must mean that the user was editing text. So now we know to switch to the edit text tab and fill in the saved text.
Generally speaking, if the desire is that you want to return the user to the same place they left off then you need to think about the state data required to get back to that point. Imagine a pristine loaded version of your app
what data needs to change to turn that into the last state the user
saw?
what data do you need to store to get back here?
This is really how android works, your activity is destroyed and recreated and it is your job to set the pieces in motion again (if you choose to do so).
Here is answer. You can save state in three different ways.
1) Subclassing app (not a good idea).
2) SharedPreferences (good for simple data, quick and reliable)
3) SQLite Database (More complex, also reliable).
Now to answer your question. There are really NO guarantees with android. At any time it can and may destroy your application without calling any particular function before it does so. So if there is data that is vital to save, the answer is save it as soon as you get it. There is usually not much advantage to saving something later, if you know you are going to need something save it immediately.
onSaveInstanceState() is just for saving temporary variables related to layout or orientation changes.
In summary persistent state/data (that should survive a crash), should be saved ASAP, don't wait for onPause(), because there are no guarantees. That's the reality.
The case I have is a game, where I want to save persistant data to a gameserver.
As this may take awhile, I find it not a good thing to try and save in onPause, but rather in onStop.
According to the tests I have done, onStop seem to be able to run in the background while onPause blocks, atleast that is the case when I press home (tested with a simple for 1 to 10m loop in onPause and onStop).
Can anyone confirm this blocking theory ?
onStop NEEDS Honeycomb up (api11+), because before that version you can get killed before onClose is called.
See here and look for killable in the table - If reality matches the documentation is another question :).
I would like to know where the bundle "outState" of the method onSaveInstanceState(Bundle outState) is stored.
Is it stored in memory or in the device storage?
I am concerned about the security of the data which is stored in the bundle.
To store data only for application lifetime (ie temporarily), use the onSaveInstanceState(Bundle) activity event
This data will only be held in memory until the application is closed, the data will be available any time that this activity starts within the current lifetime of the application.
Explanation: if data is stored here by activity A then the application shows a different activity or rotates the screen (hence closing A) and then returns to A the data can be retrieved to populate the controls. However if the application is closed and opened again the data will be gone and the controls will revert to their default values.
Example of use: storing text typed in by user and selections making up an order, blog entry, message, etc...
Note:
It’s important to notice that only the Activity is destroyed and recreated, not your whole application! An Android application can consist of many Activities, Services and ContentProviders! If the application is closed (for example by pressing the “Back” Button, then all values will be gone. savedInstaceState is only there to preserve data temporary when an Activity is destroyed/recreated, not the application itself.
If you want to preserve data permanently, you need to save it either as Preferences or in a ContentProvider/database.
Here is a detailed answer for where the outState Bundle data is saved:
...Bundles are an IPC mechanism, so it's not going to the filesystem. But now there's a P involved – which process is it? And what is that process doing with this data? And do I need to be worried about it? It turns out that these instance state bundles are stored in the Activity Manager service. This service is implemented under the package com.android.server.am in the Android source code. Recall that Activities are stacked one on top of another and that Android calls these stacks “Tasks”... Each of these tasks is represented internally with an object of class TaskRecord. This class contains an array of ActivityRecord objects, each of which manages the state of an Activity. ActivityRecord contains a member of type Bundle named icicle. This icicle-bundle is the saved instance state and it is actually stored in the memory space of the Activity Manager service.
Source: https://www.linkedin.com/pulse/android-onsaveinstancestate-bundle-secret-safe-daniel-pietsch/
The documentation has been updated and indicates precisely that the state is serialized to disk:
Saved instance state bundles persist both configuration changes and process death, but are limited by amount of storage and speed because onSavedInstanceState() serializes data to disk.
You can also found a table comparing the differents approches to preserving UI state
Source: https://developer.android.com/topic/libraries/architecture/saving-states
I don't think there's any way that any malicious background process can get at the bundle data of your application. It is not documented how Android treats the Bundle data. It may or may not be written to disk in the event that your app is cleaned, while backgrounded. However, given that we don't know whether or not this data is saved to disk, and if it is, given that we have no clue where, and almost certainly don't have read access to that part of the disk, I wouldn't worry about some third party process being able to recover that data.
Consequently I'm not clear what you might think the exposure is. Though I may be missing something.
However, in answer to your question, it is absolutely in memory while your app is alive, and if your app is backgrounded it may or may not be written somewhere hidden, but we dont' know because Google hasn't told us.
It's destroyed along with the application when the memory is collected.
My guess would be in memory, but the best way to protect your data would be not to trust the system and encrypt it. Never trust the client (in this case the client being the OS).
EDIT:
To be clear, I'm not saying encrypt the bundle. Rather I'm saying that any sensitive data should not be put into the bundle. If you must put custom data in the bundle, then encrypt it.
But ultimately you should keep as little sensitive data on the client as possible. This is the same reason a e-commerce site would only show the last 4 digits of a credit card.
I'm writing a bitmap editor where I use the Command Pattern to represent actions that will transform the document. I keep all the commands executed so far in a list and, to implement undo, I restore the document to its initial state and then replay all but the last command.
I would like my undo/redo system to have the following feature: When the user closes the editor and returns, the document, including the available undo and redo commands, should be restored to the state it was in when the user left.
I'm implementing this for Android where your application can be given very little notice before it will be cleared from memory if e.g. the user gets a phone call. Also, some of my commands are e.g. a list of all the x,y co-ord the user painted on so these might take a few moments to save to disk.
My current idea is as follows:
When a new action is performed, the command object is added to a list S for commands that need to be saved to disk.
A background thread is used that will continually take commands from list S and save them to disk. The postfix of the filenames used will be numbered in sequence. For example, if the user filled the screen then drew 2 circles, the command files might be called FillCommand1.cmd, DrawCircleCommand2.cmd, DrawCircleCommand3.cmd.
Periodically, we save a "checkpoint" command whose purpose is to store the full document state so that, even if one of the .cmd files is corrupted, we can restore a recent version of the document.
When the user exits the app, the background thread attempts to finish up saving all the commands it can (but it might get killed).
On startup, we look for the most recent .cmd file that represents a checkpoint that we can load successfully. All the .cmd files we can load after this (i.e. some files might be corrupt) go in the redo command list, all the .cmd files we can load between the first checkpoint loaded and the oldest checkpoint we can load go in the undo list.
I want the undo limit to be about 20 or 30 commands back so I need extra logic for discarding commands, deleting .cmd files and I've got to worry about multi-threading behaviour. This system seems pretty complex and will need a lot of testing to make sure it doesn't go wrong.
Is there anything in Java or Android than can help make this easier? Am I reinventing the wheel anywhere? Maybe a database would be better?
Rather than reverting to the original then performing all actions, consider making Commands reversible. That way, if you ever decide to increase the size of your undo history, you won't be introducing the potential for lag while undoing. Alternatively, as Jared Updike notes, your application may benefit from caching render results in the near past and future.
I think you're overcomplicating things with your filesystem-based solution. If you want to maintain a backup of the entire history of current working document, you should just keep an unbuffered log open in append mode, and log actions to it. The log should be associated with the particular instance of the application and file being edited, so you don't have to worry about another thread stepping on your toes. Loading from that log should be very similar to loading from an ordinary save file. Just discarding the last-read action whenever you encounter an undo action.
Well, your code is probably imperative in nature, where the state of the application is modified in place by the user's actions. This is probably fast and straightforward. Undo is basically time-travel and if you clobber old states by modifying state in place you will have to store either recipes to recompute it in reverse, or a history that can recompute it forwards.
Like you said, you can store the actions and the initial state and play them forward (stopping at the new point in history the user selects) but that means undoing one action can cause n actions to replay. One approach is to store saved state copies in the history list so you can immediately jump to a given state. To avoid using too much RAM/storage you if your system is clever it can detect the nearest (non-null) saved state in the history and recompute those few stpes forward (assuming you have all the actions you need -- this assumes actions are small and state is large(r)) until the correct state is reached. In this manner you can start eliminating old saved states (delete or set to null) (drop the state based on a cost function inversely linear to how far back in time the state is), making undo fast for the recent past, and memory/storage efficient for ancient history. I've had success with this method.