I want to save some small data structures (~1kB total), as well as the user's preferences, while my app is closed. The settings are modified only in my PreferenceActivity, but the data structures are modified in pretty much every activity. I've extended Application and made all the data structures and preferences static. I've then tried saving to SharedPreferences in my application class's onTerminate() and loading it again in onCreate(). However, onTerminate()'s documentation states that "It will never be called on a production Android device, where processes are removed by simply killing them".
Answers to this question suggest saving to SharedPreferences in the onStop() method of each activity modifying their data. Will this guarantee that the data is saved in all cases? Is there a way to avoid the waste of saving every time the user transitions between activities (or should I even care)?
I would honestly just save in the onPause() of each activity as is recommended by Android (i.e. when you write an email notice how the draft is saved when the app pauses, such as if the screen turns off). Unless you notice that it is causing lags/delays, it probably won't matter too much.
If you do notice lags with this "autosave" method, then you should probably have some sort of "Save" functionality implemented in each activity, which could, for example, entail using a ProgressDialog / AsyncTask combination.
Related
I am developing an Android application consisting of over 10 activities. I have some state objects that I access in almost every activity and for this purpose these were implemented as global static variables in the MyApplication class.
I noticed that this approach is OK as long as the user is "in" the application. However, when he presses the home button and opens another apps and then goes back to my app through the "Recent activities" button, I see that the Android system resets the statics from the MyApplication so I have to deal with NullPointerExceptions. I know that this behaviour is caused by Android killing and recreating the application process.
I know that the best way to persist this kind of data is using SharedPreferences or SQLite, and I have no problem checking if MyState==null in the onCreate for and restoring it, but the problem is that I don't know when to properly store my state object (in prefs or database). I tried to override MyApplication's finalize() - no good, I saw that onLowMemory may not be called, I don't see how can I use onPause, OnStop and so on because I have so many activities that the serialization de-serialization would considerably slow down the app.
Any thoughts?
Thanks in advance!
It is better to not depend on the Application class unless you need to load some data, before anything else is started. Android can kill your process at any time to free resources, so your app should be able to handle this. Save all of your data in a snigleton class, and load it lazily -- check for null, and if so load on first access. It the state needs to be persistent, consider staving it file/shared prefs. If not, your app can probably live without it, so just make sure you check for null, etc.
Generally, you should persist state when activities become inactive -- onStop(), onPause(), but you can save as soon as it makes sense (e.g., the user has entered all required data). Spin off an AsyncTask to save data in the background and let the user continue their work.
I have a rather small amount of state in my Android app that I wish to ensure is saved persistently. It's tiny, clocking in at about 50 characters.
Looking at the Android Storage Options page, they give a few choices, the simplest which seems to be shared preferences. Now these aren't actually application preferences, more a short indication of where the application is at.
My worry is that preferences are expected to change infrequently and the likely use case may not therefore handle a lot of changes. By a lot, I mean peaking at about once per second, depending on user activity.
So I want to make sure of two things.
The first is that the sample code on that page linked above has the preferences being written out in the onStop method. Are we guaranteed that this will run when the application exits, no matter what?
If so, I can maintain the state in-memory and use it to save persistently, then this frequency-of-updates problem disappears.
Basically, I want to ensure there's no way my application can stop without having saved its state (short of catastrophic failure of course).
Otherwise, I'd rather save this state whenever it changes so as not to lose anything.
Second, and this is important only if I have to save state every time it changes, are there any downsides to saving preferences up to once per second? As mentioned, I'm not sure if it's designed for frequent updates.
If the application exits normally, onStop() is called. If the application is backgrounded (ie with the home button/getting called) onPause() is called, which calls onStop(). If the application exits (ie with the back button), onPause() is called, which calls onStop(), which calles onDestroy(). However, when the application crashes (ie through a nullpointerException, onStop() is not called, the application just crashes).
Personally, I mostly use a settingsContainerObject of some sort, and just write my preferences to the internal storage in the onStop() method. Works pretty well.
In short: yes, onStop() is always called when the application is backgrounded or exit.
You can also stop the application in try/catch blocks, to always save your settings.
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 :).
When I want to save some state behavior of the activity, The docs says that we should implement OnSaveInstanceState and OnReceiveInstanceState.
They say that this will save the activity state even after destroy or restarts. I care more about destroy (the activity is completely gone) , does that mean the bundles are considered persistent ?
when I open a pdf reader, clost it and open it again i see that it opens in the same page I was in. is this implemented using Bundles or oth
To store persistent application data use Shared Preferences. Shared Preferences are simply sets of data values that are stored persistently. By persistence, we are talking about data that persists across application lifecycle events. In other words, the application (or device, for that matter) can be started and stopped without losing the data. The next time the user launches the application, that data will still be available.
Some Games use Shared preference for exemple to store the level of the game reached, the player's name ...
see this link to learn how to use Android Preference API
Preference are simular to bundles However they are persistant and bundle are not!!
Keep in mind that if you need to store persistent data you have 4 options to do this:
Using Shared Preferences
Using SQLite Databases
Using Internal Storage
Using External Storage
Bundles are not persistent, the documentation says not to count on it, onSaveInstanceState() is called when the activity is about to be killed by the system, but for a known restart (for
instance on a screen rotation.) If your activity is killed because the system needs more resources (while the
activity is in the background), onSaveInstanceState() will not be called, but onPause() will. onSaveInstanceState()
really is not meant to save persistent data, as the doc states.
You can sorta consider SavedInstanceState() permanent but it's not recommended to use it for saving application related data in a persistent manner as It's not guaranteed to be called and it's not recommended by the authors themselves.
so, Only use them for saving user interface state changes (background color, currently selected items ,..) and use other method for persistence like : SharedPreferences, Files and SQLite.
Bundles are not persistent, and the documentation for them specifies that using them for persistence is not a good idea as their internal format may change between devices or even OS versions.
SharedPreferences, on the other hand, can be persisted and are the recommended way to store information like current app state.
Some relevant parts from SavingActivityState
:
Note: There's no guarantee that onSaveInstanceState() will be called before your activity is destroyed, because there are cases in which it won't be necessary to save the state (such as when the user leaves your activity using the Back button, because the user is explicitly closing the activity).
There is no guarantee data will be stored, especially in the case where your user exits the app.
Note: Because onSaveInstanceState() is not guaranteed to be called, you should use it only to record the transient state of the activity (the state of the UI)—you should never use it to store persistent data. Instead, you should use onPause() to store persistent data (such as data that should be saved to a database) when the user leaves the activity.
So like K-ballo said, used SharedPreferences if you have persistent data to store. onSavedInstanceState() is mostly useful for storing UI related data.
As every one else has recommended use shared preference and you should do this saving in onDestroy and onSavedInstance both.
When android is going to run low on memory, its just going to kill your application and call your onSavedInstance without calling onDestroy etc. Save your context in bundle it passes in onSavedInstance. When your app comes in foreground again, android will take care of restoring your back stack of activities. But this time it will pass you the bundle in your onCreate for each activity which will have all the values you saved in your onSavedInstance while your app was getting killed.
Hope this helps.
Short answer: It's not permanent.
Long answer: From "Android Programming - The Big Nerd Ranch Guide":
When onSaveInstanceState(...) is called, the data is saved to the
Bundle object. That Bundle object is then stuffed into your activity’s
activity record by the OS
...
So when does the activity record get snuffed? When the user presses
the Back button, your activity really gets destroyed, once and for
all. At that point, your activity record is discarded. Activity
records are also typically discarded on reboot and may also be
discarded if they are not used for a long time.
I want to save application state to be able to restore it after another launch. Is is it better to use method onSaveInstanceState and save it to Bundle or to use SharedPreferences?
Thanks
It depends on your intention. Using the onSaveInstanceState() is only a reasonable solution if you want to ensure saving the state during configurations changes and other restarting events. In case you aim for a true saving of the application's state beyond the lifecycle of the application, you should consider using either the SharedPreferences or maybe even employ a database.
I may not have the same development chops as some of the other posters here (I've been seriously developing apps since July 2012), but I've found a solution that integrates SharedPreferences as well as the onSaveInstanceState().
My App has a splash screen activity that reads values from SharedPreferences and assigns them to the appropriate variables. Additionally, each Activity I make has its own onSaveInstanceState() method, and I commit all of the data I need to save to SharedPreferences there, in each and every Activity. Since onSaveInstanceState() is run before an App or Activity closes normally, it should back up data values under all normal circumstances.
It may not be the most code-efficient solution, especially in larger Apps with many Activities, but as far as my tests go it protects your app from data loss 99% of the time.
If a more experienced developer would like to chime in and confirm or deny this, I'm sure it'll enrich the question and answer.
I am sure onSaveInstanceState() is the better option.
Here it is already given a better explanation: Saving Android Activity state using Save Instance State