So I have been looking at tutorials and messing with things for hours. I have my main activity which creates a map of object that it gets from the database. To avoid unnecessary database queries, I am trying to save that map using the onSaveInstanceState method and then restore it with the onRestoreInstanceState. I can see that it gets saved correctly using the debugger, but if I set break points in the onRestoreInstaceState method the program never breaks. Then I thought I would just do it with the onCreate() method but it's not breaking there either. The program should break in both of those places when returning from a different activity right?
Those two methods are only called if the activity is killed and restarted.
The good news is, if your activity was not killed, there's no need to restore your state. It should still be just the way it was.
If you do need to do something when switching back from another Activity, that's what onResume() is for.
See the chart for more.
Related
I'm a little confused in trying to save values to an SQLiteDatabase. I want my app to seamlessly save the state and data, so I first put the "save to SQLDb (AsyncTask)" code into onStop(), and the "load from SQLDb" code in onStart(). However, I've noticed this is unreliable: pressing the back or home button calls onPause(), and sometimes onStop(), and when the app is brought back to the foreground, the Spinner items are sometimes there, sometimes empty, and the rest of the Views/content is also unpredictable due to whether onStop()/onStart() had been called.
So I moved the "save" code to onPause(), and the "load" code to onResume(). Now, I've read in the docs that this kind of stuff shouldn't be done in these methods, but for the most part, it works better than onStop() and onStart(). However, still, it is not exactly reliable: sometimes the AsyncTask never seems to end. If the user brings the app back to the foreground before the AsyncTask finishes, it leads to the above-mentioned unpredictable Views.
Where should I be saving this stuff?
Note: I have two Spinners that the user can dynamically add items to, as well as a GridView that must repopulate itself based on one of the Spinners (one gridChild per Spinner item). This kind of stuff, to my knowledge, cannot be saved easily by SharedPreferences or similar state-saving techniques, it must be done by database (especially since there are an indefinite amount of objects being saved, depending on what the user input).
Normally, you persist your data onPause() and restore it onResume().
This is because onStop() is not guaranteed to be called if the system needs to kill your app.
atm I try to get my app working around "onSaveInstanceState" and "onRestoreInstanceState", but the deeper I dig, the the more problems occur.
So, for example, I just realized, that restoring one activity via those two functions is pretty useless. Because, if I press the back button and return to the activity before, this one doesn't get its "savedInstanceState" bundle and instead gets recreated completely.
Is there a way to restore the whole application instead of just a single activity? Or is this just a weird design and I shouldn't even bother with restoring one activity?
Kind regards,
jellyfish
Edit: ok, stupid me...
the bundle my main activity got was not null, but only in "onRestore...". In "onCreate" it was null indeed, but this was true all the time. (No matter if I came back from another activity after kill or not, for example)
So now I'm confused in another way: I have tested this before in another activity, and there, the savedInstanceState bundle of "onCreate" and "OnRestoreInstanceState" where the same! Is this just random or something special of the main activity? (Tried different launch modes as well, but they had no impact).
So, for example, I just realized, that restoring one activity via those two functions is pretty useless.
No, it is very useful, when used properly.
Because, if I press the back button and return to the activity before, this one doesn't get its "savedInstanceState" bundle and instead gets recreated completely.
No, it doesn't.
If it already exists and is on the back stack, it will be started (onStart()) and resumed (onResume()), but it is not created. If Android had to close the previous activity (e.g., due to a shortage of memory), the previous activity will be created (onCreate()) and will be passed a Bundle containing the data it populated in onSaveInstanceState().
The only way those statements would not be true is if you are monkeying with the BACK button processing.
Is there a way to restore the whole application instead of just a single activity?
No.
Or is this just a weird design and I shouldn't even bother with restoring one activity?
You most certainly should restore one activity.
onSaveInstanceState() is used for two scenarios:
If the user changes configuration (e.g., rotates the screen), your activity will destroyed and recreated. You use onSaveInstanceState() to pass data from the old activity instance to the new one.
The BACK button scenario I outlined above.
I have tested this before in another activity, and there, the savedInstanceState bundle of "onCreate" and "OnRestoreInstanceState" where the same!
Of course. They are supposed to be the same. If the activity is being created totally from scratch, onCreate() will be passed null and onRestoreInstanceState() will not be called. But if there is instance state, that state (Bundle) will be passed to both onCreate() and onRestoreInstanceState().
Is this just random or something special of the main activity?
Neither. They are supposed to be the same.
I have finished the Layout exercise and wondering why they include the call to populateFields() in both onCreate and onResume.
According to Activity Lifecycle "onResume" will always be performed before the Activity is shown so why not just there?
I have real production code that populates fields and is only called in onResume and it works just fine.
I thought one reason would be that maybe onResume is called after the activity is shown, but a bit of googling digs this (mostly unrelated) thread:
http://groups.google.com/group/android-developers/browse_thread/thread/ddea4830bedf8c6c?pli=1
Quote: onResume() is thus the last thing that happens before the UI is shown
This is what Dianne Hackborn says so i guess we can trust her :)
Actually I have seen apps (in my app and also others), where fields were only populated in onCreate(), but not in onResume().
Lets call that app 'A'.
The effect was that when the user pressed the home button, went to a different app, and then returned to 'A', the screen stayed black, as 'A' was still in memory and thus the system did not bother to call onCreate(), but directly went into onResume().
So basically I'd say (and this seconds what #Torp wrote) populate the UI in onResume() and be done.
But then, this answer is slightly off-topic as it does not answer your "why" question.
You don't populate in onResume because it will be called every time the activity is shown.
You generally want to create as few objects as possible, so you create them once and for all in onCreate, and then you can always check that they are still updated in onResume.
After going through an introductory Android programming book, I wanted to alter the example application in order to solidify my understanding of some topics that weren't really covered. In making the change, I made an error, but I'm curious why the error worked in some cases but not in others.
An activity within the application stores a series of questions in a Hashtable<Integer, Question>, where Question is a small class holding an int and two Strings. As originally written, the activity downloads the questions from a server on every onCreate(), so I wanted to implement onSaveInstanceState() to prevent some redundant downloads. onSaveInstanceState() saves the Hashtable into the Bundle using putSerializable().
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// mQuestions is a member variable of
// type Hashtable<Integer, Question>
if (mQuestions != null && mQuestions.size() > 0) {
outState.putSerializable(SAVED_QUESTIONS, mQuestions);
}
}
It worked perfectly for screen orientation changes even before I knew what a Parcelable was or how to implement one. I only knew there was a problem when I pressed the emulator's home key and the app silently, invisibly crashed with no LogCat output. The stack trace led me to look up Parcelable and make Question implement it.
My question isn't what I did wrong. The question is this: When the Question class did not implement Parcelable, why did the app crash only on pressing Home and not on a screen orientation change?
As far as I understand Android doesn't serialize an instance state when recreating an activity after configuration changes. That's why your code works. Persisted objects just don't need to be parcelable because they exist in memory only.
This looks like an optimization. Android knows that the process will not be terminated in this case and there's no need to save the instance state to a file. (In theory the process can be terminated during the configuration change and I don't really know how Android solves this problem).
But when the user presses Home key your app becomes background. And its process can be terminated in case of low memory. Android needs to save activity's state to a file in order to be able to restore your app and its activities in future. In this case the instance state is really serialized and saved to a persistent storage. And that's why your code doesn't work.
Process termination can occur at any moment so you can't rely on some implementation details. Just make instance state parcelable or serializable and you will not face this problem again.
Quoting Steve Moseley
Note that it is NOT safe to use onSaveInstanceState and onRestoreInstanceState, according to the documentation on Activity states in http://developer.android.com/reference/android/app/Activity.html.
The document states (in the 'Activity Lifecycle' section):
Note that it is important to save
persistent data in onPause() instead
of onSaveInstanceState(Bundle)
because the later is not part of the
lifecycle callbacks, so will not be
called in every situation as described
in its documentation.
In other words, put your save/restore code in onPause() and onResume() instead!
The app did not crash. It was simply shut down when the user clicked the Home key. That's why there was no output to LogCat.
Set a breakpoint in Activity.onDestroy() to confirm this. If I'm right onDestroy() will be called but onSaveInstanceState() will not, because onSaveInstanceState() is only called when the app is placed in the background state, not when it's shut down.
If you need to save the app state upon shutdown, place the code in onDestroy() and save it to something more persistent than a Bundle.
Barry
I have two activities. One loads all rows from a database, and the other saves to the database. When I have the second save in onStop and the first repull the data in onResume, they do it out of order (the first resumes and then the second saves). I managed to fix this by putting the saving data in onPause, but why was this happening? Was this the cleanest way to do it?
Doing the save in the first actvity's onPause should be fine.
You've discovered that the foreground lifetime of an activity happens between a call to onResume() until a corresponding call to onPause(). During this time, the activity is in front of all other activities on screen and is interacting with the user.
When you start the second activity, onPause is called on the first and then interactive control switches to the second, with onStop on the first to be called somewhat in background.
This improves responsiveness and gets the new activity in front of the user ASAP. Consequently, you should try to keep your onPause implementation as fast and efficient as possible.
See the following Android docs for more details on the lifecycle http://developer.android.com/guide/topics/fundamentals.html, but what you have found should work fine for you.
Some official quote as an add-on:
The Activity documentation states that
onPause() is where you deal with the user leaving your activity.
Most importantly, any changes made by the user should at this point be
committed