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.
Related
I recently wrote a demo app, which just needed to display some data temporarily --- I meant for the data to disappear once the app was properly destroyed by the user. Toward this, I read the page
The Activity Lifecycle , which seems to recommend overriding the Activity methods
onRestoreInstanceState() and onSaveInstanceState().
It worked great! The data was preserved through screen rotations, and sending the app to the background.
But then I would leave the app running and walk away, and when I looked at it again, the data was gone.
I spent hours trying to de-bug my app, and re-reading that page.
Finally, I read
Saving UI States. It refers to overriding these methods as "ViewModel" approach, and explicitly states that data saved this way does not survive system-initiated process death --- which explains my observation.
My main question is: what on earth is the practical application of this "ViewModel" persistence approach? What is the use-case for a persistence mechanism that randomly disposes of data when the user isn't looking?
(I guess this is an old API left over from the times when apps didn't run in the background. But I don't see that reflected in the documentation.)
A second question is, reading the first page, how on earth was I supposed to understand this unfortunate behavior? Did I miss something? (It is very long.)
what on earth is the practical application of this "ViewModel" persistence approach?
It is not a persistence approach. A ViewModel is a way of holding onto state across configuration changes. Using a SavedStateHandle with ViewModel — which maps to onSaveInstanceState() and onRestoreInstanceState() — is also useful for a fairly narrow use case:
User is in your app and does something that you don't want to save to disk or the server (e.g., the user didn't click "Save" yet)
User turns off their phone screen or switches to another app (e.g., via system HOME navigation or the overview screen)
Time passes
Android terminates your app process to free up system RAM for other apps
Within ~30 minutes of having left your app, the user returns to your app
At this point, Android wants to pretend that your app had been around all along, despite the fact that your process had been terminated. So, Android will not only start up a fresh process for you, but it will recreate the last activity the user had been on... and you get your saved instance state back as part of this.
However, this is not a persistence approach. For data you want to have survive long term, you need to save it to disk (SQLite, SharedPreferences, JSON file, etc.) or to some server. Notably, if the user leaves your app for an extended period (over ~30 minutes), Android will not attempt to restore the instance state, and your app will be started normally.
You need to use a SavedStateHandle with a ViewModel to get data persistence when the system terminates your app in the background. Otherwise it's more about sharing data between components, and surviving Activity destruction e.g. on screen rotation without having to do a lot of boilerplate handling.
Just like with onSaveInstanceState, this is purely about persisting data when the system kills your running app to recover memory, so that when the user switches to the "running" app again, it can be recreated and restored exactly as it was. It doesn't save any data when the app is intentionally stopped, e.g. calling finish(), the user backing out or swiping it away etc.
This stuff should always just work - if you were seeing your data "go missing" and the app wasn't crashing in the background, it's possible your save/restore logic wasn't working. A good way to test that is going to Developer Options on your device (if you don't know how to get that do a search, it depends on your device) and enable Don't keep Activities. That will destroy them as soon as they go to the background and it should help you test how that's handled. The fact you were handling rotations ok suggests it was a background crash though, but that depends on how you were handling configuration changes
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.
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 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).