Is anyone aware of the reason why my onDestroy method to save a bool inside my shared pref works fine on vd but doesn't always work on real device?
it looks like working when i quit the app and go back further to main screen but not when i quit the app, remain in the app list window and open the app back again.. weird
is there some bug i'm not aware of or may depend on my other classes? if so i can post the code but is very simple, just as said a bool save and a get
edit: if you guys try to create a scrollview in a fragment (frag of a MainActivity), and try to go on edit mode and press back button to quit your app, mess with it a few times and you'll see onDestroy and even onStop won't be called.
Q. So how am I supposed to save my pref before quitting an app in a consistent way? reliable all the times? also for this i'd need an onDestroy alternative not even onStop, cause I need to save pref only before app quit not on activity change, and since onDestroy isn't reliable (from what Android Dev guide says) what should I use??
thanks
WORKAROUND: I placed this in onCreateView()
boolPref = getActivity().getSharedPreferences("pref_bool", Context.MODE_PRIVATE);
firstVisit = boolPref.getBoolean("bool", DEFAULT);
if (firstVisit == false) {
scrollView.setVisibility(View.VISIBLE);
}else{
scrollView.setVisibility(View.INVISIBLE);
}
if (savedInstanceState == null) {
scrollView.setVisibility(View.INVISIBLE);
}
where firstVisit is boolean firstVisit; declared at start.
(this boolean trick was part of a solution from another post, but it didn't work well saved inside onDestroy() or onStop() cause sometimes they don't get called)
then I added this inside my button onClick:
boolPref = getActivity().getSharedPreferences("pref_bool", Context.MODE_PRIVATE);
SharedPreferences.Editor editor_bool = boolPref.edit();
editor_bool.putBoolean("bool", false);
editor_bool.apply();
scrollView.setVisibility(View.VISIBLE);
and it works, what it basically does is: set my scrollview to be hidden everytime my app starts (through savedInstanceState as #bwt suggested below), and remains hidden when i pass to another activity and come back (using bool = true) unless i press the button inside first activity (setting and saving bool == false) so from then on the scrollview is always visible even if i pass to another activity and come back
I think the right moment to save a preference is when it changes, not when the application quits. Specially since Android may choose to simply kill a process (and the included application) without calling any callback. Use apply() instead of commit(), so that the application does not have to wait.
Alternatively you can override onSaveInstanceState(). It is not part of the basic lifecycle but gives you the opportunity to store the current state of your Activity before being destroyed.
Related
I'm making an app that has just one Activity. When I press the Home or Recent Apps button, the state is correctly saved and when I go back to the app everything is there. When I press the Back button and return to the app later, savedInstanceState is always null in onCreate. I've read that the state is not saved when the back button is pressed, but this seems unintuitive in my case considering that the back and home buttons are equally valid ways to leave the app in the user's eyes, until they try and go back to what they were doing. Is there no way to save state when the back button is pressed?
If I must do some persistent storage, then:
a) How do I override the back button behaviour to make it save the app when back is pressed, and
b) Can I persistently save a Bundle somehow so that I don't have to duplicate the code for state saving?
What you are searching for is SharedPreferences I think take a look at it. It will help you solving your problem
I may be wrong, but my understanding is that savedInstanceState only saves your data when you change your device orientation, but as soon as you click back button, your activity is destroyed. In order to save your data when back button is pressed, you could save your data by using SharePreferences or you could use singleton class.
http://developer.android.com/reference/android/app/Activity.html take a look at an activity lifecycle first. Sounds to me like you need to override the ondestroy method and persist your stuff.you can use a bundle for simple stuff but complex stuff it's best to use parcel.
The oncreate method should check stuff to see if it's null
I want a notification to pop up when an app opens. But once User dismisses, I don't want it to come back again, even if they go back to the same activity. But when the app exits, and they come back later, I want same dialog notification to pop up (prompting user to login).
So basically...
boolean b = true;
if (b == true) {
// show dialog
b = false;
}
I simply want var b to save state but clear on exit.
I'm not sure there's a point to using sharedpreference if you don't want the value to persist across your app being closed, as that's kind of the point of them.
Perhaps just using a boolean in your Application class would be better? It will be persistent until the app is completely shutdown.
android has no concept of "app exit", only the android lifecycle, so you'll have to be more specific about the behavior you want. for example, do you mean whenever the user causes any activity in your app to be paused and no activity in your app to be resumed?
if you only have one activity, then you can just keep a state variable in the activity setting it to false when paused and true when resumed.
if you have >1 activity, keeping track of when none of your activities are in the foreground any longer is painful. for example, if you set foreground=false whenever an activity pauses, and foreground=true whenever it's resumed, and then check a time tolerance. e.g., if the time between pause and resume is greater than some threshold, assume you are coming back from the background.
You don't want to use SharedPrefs unless they logout out of app with a button or some other listener, you can't be sure to change it. You can put it in onDestroy() to reset the variable or onPause() even better if you want to make sure it gets reset
The books I have are abysmal at explaining how to work with the lifecycle, there's a lot I'm missing that I'm hoping somebody can fill in.
My app structure is that when it's first started, it starts an activity full of legalbabble that the user has to accept. When he says 'ok', I start my main activity and then I call finish like this:
public void onClick(View view) { //as a result of "I accept"
Intent mainIntent = new Intent(mParent, EtMain.class);
startActivity(mainIntent); // Start the main program
finish();
}
Then in EtMain in the onCreate method, I've got some tabs and I instantiate some classes:
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mTabHost = (TabHost)findViewById(android.R.id.tabhost);
SetupTabs.setMyTabs(mTabHost, this);
mComData = new ComFields(this); // Create the objects
mDepWx = new WxFields(this, DepArr.Departure);
mArrWx = new WxFields(this, DepArr.Arrival);
mDepVs = new DepFields(this);
mArrVs = new ArrFields(this);
mTabHost.setOnTabChangedListener(new OnTabChangeListener(){
}
Questions:
The 'finish' in the first fragment should terminate the legalbabble activity so it'll never be restarted, right? And the EtMain one will remain forever (until killed externally), even if my app gets pushed to the background, right?
The way it is now, when EtMain gets pushed and later brought to the foreground (by tapping on the icon), it goes through the legalbabble screen as though it's a complete start - that's what I'd like to prevent - going thru the legalbabble screen again.
It would seem that I'd want to override onRestart in the second code fragment and put something in there to restart the app, right? That's the part I'm unclear about.
My question then is what needs to be done in onRestart. Do I have to recreate all the tabs and data in the tabs and all my object instantiations? Or is the memory state of the app saved someplace and then is restored back to the state that it was in before something else was brought to the foreground in which case not much needs to be done because all the objects and listeners will still be there?
Yes after the first activity has ended you shouldn't have to view that activity again. You could also write to the shared preferences that the user has previously seen legal info.
If you're UI object creation is in the onCreate method, this should only be called once. Pausing or resuming will not call the onCreate method again.
Unless you explicitly remove your objects and tabChangedListeners in the onPause method, you should not have to touch them in the onRestart method.
Correct, the state of the app is saved automatically. You shouldn't have to touch the onRestart method.
Hope this helps!
I think the problem is that the launch activity in your manifest is the legalbabble activity, so when you click on the icon, the system launches another one. A better architecture would be to launch the legalbabble activity it from your EtMain activity in the onCreate method of the latter, using startActivityForResult. From the docs:
As a special case, if you call startActivityForResult() with a requestCode >= 0 during the initial onCreate(Bundle savedInstanceState)/onResume() of your activity, then your window will not be displayed until a result is returned back from the started activity.
When you get the result in onActivityResult, you can call finish() if the legal stuff was declined; otherwise everything will proceed normally.
This avoids the problem that the launch activity defined in your manifest finishes when the legal stuff is accepted.
EtMain will not remain forever, if the user backs out (by pressing the BACK key) the Activity will be finished (onPause, then onStop, then onDestroy will be called).
In general you can ignore onRestore until you are doing something complicated.
Once the user has exited your application and re-enters (or presses the icon on the Homescreen), onCreate (followed by onStart and onResume) will be called for your first activity, so you do not need any logic in onRestart, your code in onCreate will do the setting up for you as it did the first time. Because of this your legal babble will appear again when the user starts the app after exiting unless you store a preference (in SharedPreferences or a database or file) to indicate you have already displayed it - in which case finish it straight away and start the main activity.
onRestart is only called when the application goes from the stopped state (onStop has been called but not onDestroy) to the started state (onStart is called but onResume has not yet).
For saving data - some components save their state automatically (e.g. EditTexts remember the text in them, TabHosts remember the currently selected tab etc). Some components will not. If you wish to save extra data then make use of onSaveInstanceState and onRestoreInstanceState. You should only use these methods to restore the state of your application or temporary data, not important things, e.g. the id of the resource what the user was looking at, what zoom level they were at etc. For things like contacts or actual data you should commit these changes to a database, SharedPreferences or other permanent storage (e.g. file) when onPause is called.
I recommend taking a look at the Android Activity lifecycle if you are confused. Or ask more questions!
In my app, some settings can possibly be changed while the PreferenceActivity is not open, and an issue I'm running into is that addPreferencesFromResource is called in onCreate, so say, I open the PreferenceActivity, then go to another screen from there, then do something that changes the settings, then hit the back key to go back to the PreferenceActivity, then certain settings have not changed on the layout.
So, how could I re-load all the Preferences every time onResume (or onStart()) is called without duplicating the layout?
edit: This solution will work for API 11 + only.
Im not sure I fully understand your problem, but you could add a call to recreate() into the onResume of the activity which from my understanding has the activity go through the entire lifecycle again.
In order to make sure that you only do this when there is in fact dirty data, I would set a flag in the SharedPreferences that lets your activity know in the onResume() that it needs to be recreated.
public void onResume(){
super.onResume();
SharedPreferences pref = getApplicationContext().getSharedPreferences(Constants.PREFS_NAME, Context.MODE_PRIVATE);
if(pref.getBoolean("isDirtyPrefs", true))
recreate();
}
I had a similar problem. Failing to find a simple way to make my PreferenceActivity refresh itself, my solution was to add this to my PreferenceActivity:
/**
* Called when activity leaves the foreground
*/
protected void onStop() {
super.onStop();
finish();
}
This will cause the Prefs screen to reload from SharedPreferences next time it is started. Needless to say, this approach won't work if you want to be able to go back to your preferences screen by using the back button.
I've heard that pressing the back button will essentially cause the current Activity to finish(). Is this always the case? Seems like it would be with the way it pops the Activity off the stack.
The one situation I'm not so sure about is when the root Activity in a Task has back pressed. I'm currently experiencing a very weird effect, described as follows:
On loading my application, the first Activity is for initialization, and once it finishes, it calls my main Activity (a TabActivity). This first init activity has android:noHistory="true" set in the Manifest so pressing Back from my main Activity won't go back to that. It goes to the Launcher. When I click on my App in the Launcher a second time, the initialization activity loads again, and loads the main Activity when done. Almost immediately after, it loads a second instance of my main Activity. But ONLY after the Application has already been run once, and was exited by pressing BACK from the main Activity. It does it every subsequent time until I force quit the app or load a new version from the IDE.
Based on this, I am suspecting some kind of Activity instance is lying around and being reused, since it only happens on the second+ time I run the application (and exit with BACK -- using HOME just returns to the last state of the app, no big deal). Anyone have any thoughts??
I've heard that pressing the back button will essentially cause the current Activity to finish(). Is this always the case?
No it is not. The most activities have this behaviour but not all. For example you could create a Dialog and set it setCancelable(false) and it won't close if you click BACK button.
Furthermore you could customize activity behaviour on BACK button pressed by overriding onBackPressed
Called when the activity has detected the user's press of the back key. The default implementation simply finishes the current activity, but you can override this to do whatever you want.
About your application behaviour..Did you verify if the activity launcher is finished after it loads your main activity? I mean if the onDestroy() method is called. Maybe after it runs the main activity it remains there and when you click back you just go back to the old Launcher...
hope this helps..
Read through the Activity and Task design guidelines on the Android developer site; they explain how the Home and Back buttons work. Obviously, if you override the default behavior (as mentioned by hara above), the back button will not finish the activity.
On your specific issue, check your logcat. You should be able to see there whether it is bringing an old process back to life or starting up a new one. If that is unclear, insert a couple of log statements into onCreate, onPause, onDestroyed, etc., so that you can see exactly what is happening with your process.
You can control BACK-BUTTON by writing the following code.
public boolean onKeyDown(int keyCode, KeyEvent event)
{
if (keyCode == KeyEvent.KEYCODE_BACK ) {
//preventing default implementation previous to
//android.os.Build.VERSION_CODES.ECLAIR
return false;
}
return super.onKeyDown(keyCode, event);
}
Are you running your activities with any special flags, such as singleInstance or singleTop? Those could be causing the oddities you're seeing. The easiest way to track down what's causing your problem is to absolutely fill it with debugging messages. For example:
In your initialisation activity, add a log in the beginning of onCreate to get the name of the activity such as this.toString(). More on why you want this line later.
When it launches the main tabbed activity, get the name of the launching activity and a message saying it's launched the tabbed one.
Override the onPause(), onStop() and onDestroy() callbacks and add debugging lines with this.toString() and also a message telling you which callback it is.
What this will do is tell you whether you've got multiple instances of the initialisation activity lying around. To this by comparing the name of the activities calling your main activity with the ones that were just created and the ones that went through to onDestroy.
If you don't know how to debug, use Log.d(LOG_TAG, "Your message here");. And then define a constant LOG_TAG String somewhere. After that, show the LogCat perspective in Eclispe by going to Window, show perspective (or view, don't remember exactly), other, Android, LogCat. The purpose of having a LOG_TAG constant is that you can set up LogCat to filter to that String and only show you those messages. It will make it easier to see them among the mass of system log messages.
The short answer to the original question is 'no'. This is largely because, unfortunately, not every developer follows the guidelines referenced by previous answers.
Yet the guidleines themselves mention exceptions, when the Back key should not call finish(). the most prominent exception is the Web browser, which has its own "back stack" for each window, so it must have its own custom handling of the Back key.
If there are no fragments on the back stack and a developer has not overridden onBackPressed, the activity will finish when the back button is pressed.
Here is the source code for Android 4.4.2 Activity.onBackPressed():
public void onBackPressed() {
if (!mFragments.popBackStackImmediate()) {
finish();
}
}
just override onbackpressed().. on back press this method will get execute remove super and do what u want to do.