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.
Related
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.
When a user opens preferences on my app he may make changes for example changing the app theme.
The documentation for ContextThemeWrapper.setTheme(int) says:
Set the base theme for this context. Note that this should be called before any views are instantiated in the Context (for example before calling setContentView(View) or inflate(int, ViewGroup)).
So my first thought was restarting the app onResume() when the user changed the preferences. However I noticed that sometimes the process of restarting the activity is seamless while other times the activity is closed, the home screen is seen and only after some seconds the app opens again.
I'm wondering if there is a way to change handle the preferences changes. Like for instance changing the theme after onResume without restarting the activity or restarting the activity on the background while the user is on preferences.
What's the right way to handle this?
Assuming that your Preference screen is an Activity, when the user navigated to it, MainActivity was placed on a paused state (and then probably on a stopped state). When the user navigates back to MainActivity onResume() will be called; here you can change the sate of MainActivity accordingly to reflect the preferences that were changed.
I'm assuming you are storing user's selected theme in App Preferences. So in your Activity's onCreate(), add setTheme().
public void onCreate(Bundle savedInstanceState) {
setTheme(android.R.style.Theme); // or whatever theme you're using
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
}
But changes don't take effect until the activity is restarted. So if you need to immediately apply theme changes, call recreate() afterwards applying theme:
// Might need to call them on getApplication() depending on Context
setTheme(userSelectedTheme);
recreate();
Also, as Attiq mentioned, re-creating an activity might result in loss of data fetched from network using Threads/AsyncTasks so you need to take that in to account as well.
When a user opens preferences on my app he may make changes that mean I have to restart my MainActivity however I don't want to user to notice anything happened.
User will not notice anything because, the activity life cycle will take care.
Tasks - EVENT 1 Main Activity (MA) TO Preference Activity (PA)
EVENT 2 Preference Activity (PA)TO Main Activity
Activity Life Cycle - MA onPause() TO PA onResume() ... PA onPause() TO MA onResume()
nor remove it from the back stack.
If you want the same activity from backstack to resume and not a new MainActivity to be created, this can be done using launchmodes
Code sample
intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
startActivity(intent);
Our application is offering multiple themes and we have already tried methods like updating themes from onResume or onStart[restart activity case] but it's requires onCreate() method calls to update the themes, you need to finish the activity and recreate it using selected themes.
Important point: Re-creating new activity might result in lose of data fetched from network using threads or asynctask in the activity so you need to take this issue as well.
In our application, we are saving the number of selected theme in the sharedpreferences and loading it during the creation of new activity.
To finish and restart the activity
public static void changeToTheme(Activity activity, int theme) {
sTheme = theme;
activity.finish();
activity.startActivity(new Intent(activity, activity.getClass()));
}
I have 2 Fragments. 1st is for calculation while the 2nd is for settings.
I save the settings using SharedPreference.Editor.commit() on onPause() method. No problem so far with the saving. The problem I am facing is retrieving the SharedPreference value on my 1st Fragment. I retrieved the value every time I pressed the count button. For the 1st time, the values I am getting are the ones before change (which is the problem I am facing), I will only get my saved value when retrieving/pressing the count button for the 2nd time or more.
And I try to change the settings and then press home button to terminate the app from outside (which triggers onPause method) and when I reopened the settings, the values did change to my defined settings. So, I am sure the settings did saved when onPause is triggered.
I wonder what is going wrong here. Any helps is much appreciated.
As requested, this is my saving code on my SettingFragment :
#Override
public void onPause() {
super.onPause();
saveToPref();
}
public void saveToPref() {
SharedPreferences settings = getActivity().getSharedPreferences("mysettings", 0);
Editor edit = settings.edit();
edit.putString("begin", String.valueOf(ibegin)).putString("end", String.valueOf(iend)).commit();
}
Initialize your SharedPreferences object somewhere else, and do it once - maybe in onCreate. The reason why you're not seeing the change is because a different instance of SharedPreferences is opened somewhere, with the same constructor, and multiple instances of these do not automatically resolve/merge. While you're at it, initialize the Editor edit instance along with settings.
Ok, I have found my problem. The problem is that onPause() is triggered only when I start another activity (I started an activity onClick of Button count). I changed my code to trigger the saveToPref() by overriding onPageSelected of my ViewPager and on backPressed of my MainActivity.
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!
I will try to explain a simple app scenario: My app goes right to a 'main view'. In this main view I have inserted a TextView which displays current settings created by way of the PreferenceManager. For simplicity sake, let's say I have a checkbox in my settings. When I first start my app - the TextView on my main view shows my checkbox setting correctly (true). Now I go to the settings menu, it pops-up, and then I change it to false. I go back to the main view and see no change. It still say's true even after I changed it to false. If I end the app and then re-start it - all is well and it shows my change.
Apparently the main view just stays in the background while I'm changing settings? How can I redraw or update the main view after I change settings?
You could implement OnSharedPreferenceChangeListener in your main Activity:
#Override
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
if (PREFKEY_OF_INTEREST.equals(key))
updateSomethingInMainView();
}
and in onCreate() call:
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
prefs.registerOnSharedPreferenceChangeListener(this);
When you go into your preferences your current activity should be paused and the resumed when you go back (see the lifecycle diagram on lifecycle diagram on the android site). You should be able to trigger some kind of redraw from within onResume() I would think. That will allow the data on the page to repopulate. Some sort of invalidate() call would do it I guess.