Implementing a robust persistent undo/redo feature - android

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.

Related

Ideal way to tackle pending data changes (when to save changes)?

I have a rather peculiar case on my hands, and Im surprised that noone seems to have written about it/something similar for Android (or my Google skills suck)
Situation #1:
User can input text into field1 and field2.
User can also re-arrange items in a list (displayed in a RecyclerView)
Whenever the user does any of the edits, the UI is already showing the updated data (e.g. editing field1 will show the text as the user types it, and the list of items will show them in the new order as the user re-arranges them).
Saving the data right away here will trigger the UI to refresh (to display the same thing) and give the user a bad experience (field1 focus will shift to the first letter, and the app might crash if the user quickly re-arranges list items).
So it makes sense to store the edits and execute them at a later point.
Situation #2:
User can tap plus/minus buttons to increase/decrease a value
User can input text into field3.
As in situation #1 above, editing the field will already have the UI in the updated state. But, in this case - tapping the plus/minus button will also update the data, but the UI will not be updated (unless the data is saved, and the query ran again...).
Problem:
If data is saved immediately as the user performs an edit, besides doing a lot of saves, it makes for a bad user experience as the UI will refresh in some cases whereas its already up to date.
If the edits are tucked away and performed at a later point, the UI wont refresh.
Im using MVVM, so all performed actions are sent to the viewmodel and it decides what to do. I find myself looking for a solution that works differently across different screens of the app, but I know that would just be shooting myself in the foot and then jumping off a bridge. Surely, there must be someone out there that has come across this challenge and had some insights around it?
Ideal solution: One solution that just works for all the different screens. Do you have it? Please let me know.
/ Desperate Android Dev
First of all, let me start by stating that I don't think there is a correct answer here, but you should consider what your own app does and then determine what you need to do.
Let me explain. Consider two application, one that saves TODO items and the other is a banking application.
I will now explain what I think could work for your application, since you have not mentioned explicitly any requirements that contradicts that.
In situations like that, I believe being optimistic is a good idea. Assuming that things will not fail, and when they do, try to back out. What does that mean?
That means, for example, in the scenario you mentioned, user enters something in a field. You should let the UI update automatically (Nothing we do here, that's just Android), when that happens you save those changes either locally, or to a server, doesn't matter.
Of course, you can optimize, instead of saving each letter, throttle the input somehow, but you get the idea.
This can either be a succeed or a failure, because we are optimistic, we let the UI update and the user get the feel that our application is lighting fast. You don't need to reload anything, or refresh anything. The UI should match your Model state now already.
What if the things go south, and your HTTP request or DB update fails for some reason, then you need to take action. But try to keep your reaction appropriate.
You can handle that failure in so many ways, again, depending on how critical what you are doing in your app really is.
You can just show a Toast, or even do nothing if the user action was so trivial.
You can show the user something a bit more concrete if the action is of some significance, maybe a Snakbar with retry and explanation of what happened.
You kill your process and finish all activities -kidding don't ever do that- but showing a very intrusive pop-up and possibly reverting the UI value to the correct one, if what the user was doing is quite critical.
Now this approach doesn't just give the feel that the app is really fast, but it also keeps things simple.
Another advise is don't try to solve problems that don't exist yet, that means don't start implementing background services and job queues for some local persistence jobs that never outlive a view, and could never will.
Instead, use measurements, log those errors and failures with some tool, and use those stats to know what needs to be fixed -if any-
Back to our two applications, this approach might be perfect for a TODO app, however this might not be too good for a banking app.
Assume the user transfers money, we say immediately, ALL GOOD MATE! and then the request fails and your user's landlord kicks him out for never paying rent.
So it all comes to how sensitive the operations your're doing.

Preventing data corruption when app exits unexpectedly

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.

When should I delete temporary image files?

At some point in my app, I have to save some temporary image files. I've tried using file.deleteOnExit(), after some research I found out it does not ensure the file will be actually deleted. So I tried to follow one of the alternatives listed on deleteOnExit() documentation that says:
• Maintain your own set of files to delete, and process it at an appropriate point in your application's lifecycle.
I am wondering, what is this appropriate point? I've tried to delete a set of files on application.onTerminate(), but the result is the same, if the app process is killed, it'll never reach onTerminate() method.
In order to keep performance and delete temporary files safely, when (where) should I place my code to delete them?
As soon as you are sure you won't use them, but no sooner.
More seriously, you should store them in the cache directory. You can get that with Context.getCacheDir(). That way if your cleanup methods don't end up getting called, the user can delete them from the Applications menu, or the system may delete them if space is required.
You can (and should) try to delete them in onDestroy() or even onPause(), depending on your implementation, but those are also not guaranteed to be called.
You might also put a timestamp in the filename, and run a cleanup periodically with AlarmManager and BroadcastReceiver by comparing the filename (which should reflect the last time that file was accessed, ideally) with the current time, and deleting it if the difference is greater than some constant. For performance sake I would probably run this daily or a couple times a day, and also (in a background thread) when the app opens or the relevant activity starts.

"persistent state" vs. "current state"

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 :).

How to save the user's selection for every time they run the app on Android?

The first time the app is run, I want to have a checkbox list appear where the user selects the list items that they are interested in, and run the program based on that. Every subsequent time the app is run, I want those selected settings to be remembered, or changed with an options menu, in which case the new settings will be remembered. But all I know how to do is make an app go from the beginning every time it is run...
Similarly, my app reads sequentially through all the data in a large, read-only, unchangable database. As of right now, it creates and opens and fetches all the data every time, which takes a few seconds at the start of the program to open up and do anything. Is there a way to get around this, or, is it even a good idea to try to get around this?
To remember the users selection, have a look at SharedPreferences. You can store the selected items there.
To the database: That really depends on your app. If you need all that data at the same time in memory, I guess theres no way around loading it at the start. If you only need parts, it would be a good idea to load a part of the data when required. But without exact knowledge of your app/use case, this is hard to tell.
When you have some sort of "lag" when loading the database: Do you probably load the database in the UI-thread (= without creating a seperate thread for loading)? Thats bad practice since it blocks all UI operations. Have a look at AsyncTasks. Thats a handy class that wraps around a thread and lets you do things in the background without blocking all the UI. After it's done with its task (loading in this case) it provides you a onPostExecute() callback where you can update your UI with the loaded data.

Categories

Resources