I had bad experience with static class variables since their values are lost when the class unloads. Therefore I avoid them alltogether.
Now I am (probably overly) worried even with "normal" variables.
I'm not sure if their value also might get lost in certain
circumstances like disruptions by a call, low memory or anything else.
Can I rely on the variables hold their values 100% ? or
do I ensure some kind of valid restore for all activity variables?
Thanks!
I had bad experience with static class variables since their values are lost when the class unloads.
Classes do not "unload". Your process will be terminated sometime after you have nothing in the foreground, when Android needs to reclaim memory.
Can I rely on the variables hold their values 100% ? or do I ensure some kind of valid restore for all activity variables?
Activities are notified of when they are moved off the foreground by a call to onPause(). From the standpoint of that activity, any time after onPause() until (possibly) a corresponding onResume(), the process may be terminated and the activity be lost.
You need to sit back and think about your data model. Suppose the user leaves your app (e.g., presses HOME) and does not return to your app for an hour, or a day, or a month. Any data that the user would reasonably expect to stick around for that period of time needs to be saved in a persistent data store, such as a database or flat file. It is your job to determine when that data gets saved -- perhaps it is when the user presses a Save button, or perhaps it is in onPause() of an activity, or perhaps it is at some other time.
Data that is tied to the current contents of the screen, but does not need to be saved for a month of absence, can be held onto via onSaveInstanceState(). Hopefully you are already using this to handle screen rotations. If so, and if the user leaves your activity but in a fashion by which they might navigate back to it via the BACK button (e.g., a phone call comes in, then a text message comes in, then they click on a link in a text message and bring up the Web browser, and later BACK all the way back to your app, which had been terminated in the meantime), your saved instance state will be restored.
Everything else -- instance data members of an activity, or static data members, or whatever -- may get lost if the user leaves the app, if Android elects to terminate your process. Hence, static data members are typically only used for short-term caches or for things that do not matter if they are lost when the user presses HOME or takes a phone call or whatever.
If you have data in your activity that needs to be saved, implement onSaveInstanceState.
http://developer.android.com/reference/android/app/Activity.html#onSaveInstanceState(android.os.Bundle)
In your onCreate, if the bundle passed in is not null, you can assume you had some state saved in there, and restore from that.
Normal variables persist, but the problem is that you can never be sure when you're onResuming and when you're onCreating (since you have no control over when Android just goes and tosses stuff on the stack out the window... anything not currently being used is eligible for destruction).
So... your best bet is to save things in whatever manner makes sense if you REALLY need them between places where someone might logically put the device to sleep, or rotate it, or get a phone call, or anything else that interrupts its TOP(ness).
I don't really like the way the bundle works, so, I've been storing my stuff in a JSONobject that I convert to string and just save as one flat SharedPreference String (note that SP persists for ever, whereas your Bundle is gonna be tossed along with everything else once your Application is GCed). That way I can just grab that whenever I want, rather than having to futz around with a billion different Bundle elements, but that's obviously a matter of taste. There's a little more work up-front, and of course slightly more overhead in the serialization/deserialization, but since it's only happening on the rare instance that my variables are destroyed, it's really not anything to worry about (unless you have some massive amounts of data, in which case you should be using a database, anyway).
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.
I'm having an issue with an android app I'm writing that seems like it should be a common issue but I can't find any information on it.
I have a scoreboard activity in my app that just stores a grid of scores in textviews, it's the kind of thing that the user will update, then hit the back key and look at some other activities, then come back later to update it, leave again, etc...
The problem is that every time they leave and come back the whole activity gets reset, losing all their scores. I can't use saveInstanceState because it isn't called on back key pressed. I really don't know where to go from here except for saving thew hole grid in sharedpreferences, I feel like there has got to be a better way though
Any ideas?
In general, you need to save any state information in onPause(), and recover it in onResume().
I was under the impression that various state information is kept automatically when you close the App, and automatically restores itself when start it back up again (until Android removes the App from its memory to make space, calling onDestroy()).
If I were you, I would store the grid in SharedPreference. It really is the most reliable solution.
You can also use the details in this topic:
https://stackoverflow.com/a/151940/503508
May this help you
When the user presses the BACK button, your foreground activity is destroyed. That activity will be called with onPause(), onStop(), and onDestroy(). That activity object will then be garbage collected (unless you introduced a memory leak).
onSaveInstanceState() will be called periodically if there is a chance that the activity will be destroyed shortly but in a way where the user might be able to navigate back to it. The prominent case for this is during a configuration change, such as rotating the screen.
What you should be doing in onPause(), if anything, is persisting data using "sqlite, file saving and other persistance methods". Once onPause() is called, there are no guarantees that this activity will stick around, or that your entire process will stick around. Anything you value, therefore, should get written out to persistent storage.
The "state" for onSaveInstanceState() would be things that affect the activity's UI but are not part of the persistent data model. Much of this is handled for you automatically by Android's built-in implementation of that method (e.g., the text in an EditText), but you can add your own information to the Bundle if you wish. However, your instance state is not your data model, so anything you want to stick around needs to be written out to persistent storage.
The documentation suggests that the data should be committed/read in onPause()/onResume().
Yet when the application is no longer in the foreground, its data structures remain intact, which suggests that one could delay committing/reading data until the application is no longer visible, i.e. in onStop()/onStart(). Particularly since onStop() is guaranteed to be called before onDestroy().
Is it perhaps the case that either approach is suitable? Is the documentation giving here merely a guideline?
Update
Suppose your application needed to save relatively substantial data, say edits to a large image. One would then surely not write/read in onPause()/onResume(), lest the user experience become sluggish. One would in that case choose instead to write/read in onStop()/onStart(). Is that true?
The problem with using onStop is that you have no guarantees on when it will be called since the only sure thing is that it will be called before onDestroy. If you wait until onStop to commit your data it may be to late for another activity to show/use any of those changes. Same thing applies to onStart, your activity may not need to be restarted if it was just in the background so you'll have stale data. Using onResume and onPause guarantees that your data will always be current, commits are made as soon as the activity goes to the background and new data is loaded as soon as it becomes visible.
Yes, it is just a guideline (and generally a good one). It is up to you exactly when you want to commit changes. I personally like to create Store Objects that allow a simplification of Databases or SharedPreferences, and when a change is made, I commit those changes immediately. For simple data storage, this will be quick and invisible to the user. For large data sets, this may take more time, and you may wish to make those writes on a time interval, as well as in onPause.
As for when to read - you can read whenever, but again longer reads will often affect the user experience, unless you have taken care of it in another thread, such as with an AsyncTask.
To Further answer your update:
It depends on the developer, however I would write in onPause() and if necessary, read in a separate thread, probably initialized with onResume(). I may also write data out on a scheduled interval using a Timer thread, depending on how it would affect the user experience for the current session, and if it would be catastrophic for the phone to turn off and lose all data before onPause() is called.
The true answer to this is, onPause is the only method you are guaranteed to get called before Android can destroy your process. If a user leaves your app to take a phone call, and Android decides to close your process, its entirely legal for you to only get an onPause call. If you hadn't saved your state there, when the user hits the back button, you will end up recreating your activity in a different state than the user left it in.
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 :).