I have a PreferenceActivty in my Android app, which due to compatibility reasons I use via the getPreferenceScreen() method and some Preference objects which I create in code, mostly CheckBoxPreference and SwitchPreference.
Up to the previous version of my app there were 8 preferences in total and everything worked fine, but now I added 2 more preferences and I'm experiencing a REALLY weird issue.
The second preference on the screen is a SwitchPreference. When I open the activity, it is checked. If I scroll down the screen without actually changing anything, suddenly its value is automatically set to OFF. I tried adding an OnChangeListener to the Preference and implementing OnSharedPreferenceChangeListener, but the results are the same: once that particular Preference disappears from the screen, it is turned OFF. If it's set to OFF, it keeps its value and the change listener is not called.
Does anyone have any idea as to why could this be happening? I'm completely lost...
Thanks in advance!
The code for my preferences is basically this, repeated 5 times for 5 different settings, on the onCreate method:
controlWifiPreference = new CheckBoxPreference(this);
controlWifiPreference.setKey(Constants.PREF_1_KEY);
getPreferenceScreen().addPreference(controlWifiPreference);
wifiPreference = new SwitchPreference(this);
wifiPreference.setKey(Constants.PREF_2_KEY);
getPreferenceScreen().addPreference(wifiPreference);
Since the preferences are inside a TabActivity, on the onResume method I call setChecked() for every preference to set its value again, though I'm not sure that it's completely neccessary.
And, finally, I have an onSharedPreferenceChanged method that activates/deactivates preferences when others are clicked, because I couldn't get the setDependency method to work. It's something like this (again, repeated five times):
if (key.equals(controlWifiPreference.getKey())) {
wifiPreference.setEnabled(controlWifiPreference.isChecked());
}
Turns out it was an Android bug in the SwitchPreference class. Someone (who I'm VERY thankful to ;)) reported it to b.android.com and even posted a workaround. It's all here: https://code.google.com/p/android/issues/detail?id=26194
How you implemented preferences inside TabActivity?I checked your code in my own IDE inside a PreferenceActivity and its working like a charm.If you need to have some pseudo prefences inside your activity, you should not use prefernces and instead, you will need to use normal forms items and save their values to the preferences manually.here is the code i tested and its working ok:
public class PreferencesFromCode extends PreferenceActivity implements
OnSharedPreferenceChangeListener {
private SwitchPreference switchPref;
private CheckBoxPreference checkboxPref;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setPreferenceScreen(createPreferenceHierarchy());
}
private PreferenceScreen createPreferenceHierarchy() {
// Root
#SuppressWarnings("deprecation")
PreferenceScreen root = getPreferenceManager().createPreferenceScreen(
this);
// Inline preferences
PreferenceCategory inlinePrefCat = new PreferenceCategory(this);
inlinePrefCat.setTitle(R.string.inline_preferences);
root.addPreference(inlinePrefCat);
// Checkbox preference
checkboxPref = new CheckBoxPreference(this);
checkboxPref.setKey("checkbox_preference");
checkboxPref.setTitle(R.string.title_checkbox_preference);
checkboxPref.setSummary(R.string.summary_checkbox_preference);
inlinePrefCat.addPreference(checkboxPref);
// Switch preference
switchPref = new SwitchPreference(this);
switchPref.setKey("switch_preference");
switchPref.setTitle(R.string.title_switch_preference);
switchPref.setSummary(R.string.summary_switch_preference);
inlinePrefCat.addPreference(switchPref);
/*
* The Preferences screenPref serves as a screen break (similar to page
* break in word processing). Like for other preference types, we assign
* a key here so that it is able to save and restore its instance state.
*/
// Screen preference
PreferenceScreen screenPref = getPreferenceManager()
.createPreferenceScreen(this);
screenPref.setKey("screen_preference");
screenPref.setTitle(R.string.title_screen_preference);
screenPref.setSummary(R.string.summary_screen_preference);
return root;
}
#Override
protected void onResume() {
super.onResume();
PreferenceManager.getDefaultSharedPreferences(this)
.registerOnSharedPreferenceChangeListener(this);
/*
* getPreferenceScreen().getSharedPreferences()
* .registerOnSharedPreferenceChangeListener(this);
*/
}
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
String key) {
Log.i("ghjg", "changed key is : " + key);
if (key.equals(checkboxPref.getKey())) {
switchPref.setEnabled(checkboxPref.isChecked());
}
}
}
However you may override onContentChanged() and see what happens.
Related
I'm getting a compiler error cannot resolve method findPreference when I try to intialize an OnSharedPreferencesChanged listener in my MainActivity. According to the answer here:
findPreference() should be called from a class implementing PreferenceActivity interface
but I don't understand what the code to do this would be. How can I get rid of the compiler error and successfully set listeners for preference changes?
MainActivity.java
public class MainActivity extends FragmentActivity implements SharedPreferences.OnSharedPreferenceChangeListener {
private SharedPreferences.OnSharedPreferenceChangeListener listener;
#Override
protected void onCreate(Bundle savedInstanceState) {
...
//Test preference menu
listener = new SharedPreferences.OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
if (key.equals("pref_wood")) {
Preference woodPref = findPreference(key); //COMPILER ERROR HERE
MainActivity.getGLSurfaceView().setTexture("");
// Set summary to be the user-description for the selected value
woodPref.setSummary(sharedPreferences.getString(key, ""));
}
}
}
}
}
findPreference is a method which is part of both PreferenceFragment and PreferenceActivity - these are the Fragments/Activities that actually show your Preference screen (the activity is deprecated and you should be using the PreferenceFragment).
You're trying to use it in your MainActivity. This doesn't work because the Preference objects don't actually exist on this screen (they exist in another activity that usually have a PreferenceFragment as part of it). If you need to get access to a preference value of a preference in an activity that is not your preference screen, use SharedPreferences, something like:
SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
int defaultValue = getResources().getBoolean(R.bool.saved_high_score_default);
boolean wood = sharedPref.getBoolean(pref_wood, defaultValue);
You can check out the documentation for further examples.
If your MainActivity is supposed to be a screen that shows settings, then you should probably rename it and include a preference fragment inside of it.
I believe you're also going to run into trouble with setSummary because the Preference is not part of this activity, it's part of the activity where you actually modify the preferences. setSummary is used to update the actual UI of the Preference so that if you, for example, select one of three values when using a list preference, it shows the value you just selected on the screen.
I use a Preference in a PreferenceActivity to load default values: when this specific Preference is clicked, something like this happens:
private String mResetKeys = "key1,key2,key3";
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
SharedPreferences.Editor prefs_editor = prefs.edit();
for (String current_pref : mResetKeys.split(",")) {
prefs_editor.remove(current_pref);
}
prefs_editor.commit();
But afterwards, the Preferences whose corresponding SharedPreference was reset still show the old value - it seems to be cached in the Preference. Only when I leave the PreferenceActivity and reopen it, the Preferences show the new values.
How can I update the PreferenceActivity programmatically?
I had a similar problem. This probably isn't the most correct fix but it worked for my purposes. Right after I did the commits, I called the Activity.recreate(); method.
The activity will restart (onDestroy()/onCreate()/etc) but for my purposes all I needed was a special handling on one preference. I listened for a certain preference with an OnPreferenceClickListener and made an alert dialog box with a kind of warning message and an option to change their mind. If they did want to change their mind, I did my commit of the new value to the preference activity and then called recreate() so that the checkbox preference would be updated.
However, I am also interested in a way to do this without recreating the activity...
Update preference value without reloading PreferenceActivity
from http://liquidlabs.ca/2011/08/25/update-preference-value-without-reloading-preferenceactivity/
Here is how to update default shared preference value of target element (in this case EditTextPreference)
public class YourCustomPreference extends PreferenceActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.preferences);
}
// some logic goes above, when you want to reset value and update EditTextPreference value
// For convenience, I am going to wrap two different task in different methods
private void resetPreferenceValue() {
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this.getApplicationContext());
SharedPreferences.Editor prefEditor = sharedPref.edit(); // Get preference in editor mode
prefEditor.putString("your_edit_text_pref_key", "DEFAULT-VALUE"); // set your default value here (could be empty as well)
prefEditor.commit(); // finally save changes
// Now we have updated shared preference value, but in activity it still hold the old value
this.resetElementValue();
}
private void resetElementValue() {
// First get reference to edit-text view elements
EditTextPreference myPrefText = (EditTextPreference) super.findPreference("your_edit_text_pref_key");
// Now, manually update it's value to default/empty
myPrefText.setText("DEFAULT-VALUE"); // Now, if you click on the item, you'll see the value you've just set here
}
}
I've implemented onSharedPreferenceChanged in my main activity.
If I change the preferences in the main activity, my event fires.
If I change the preferences through my preferences screen (PreferenceActivity) my event does NOT fire when preferences are changed (because it's a separate activity and separate reference to sharedPreferences?)
Does anybody have a recommendation of how I should go about overcoming this situation?
Thanks!
EDIT1: I tried adding the event handler right in my preference activity but it never fires. The following method gets called during onCreate of my preference activity. When I change values, it never prints the message (msg() is a wrapper for Log.d).
private void registerChangeListener () {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
sp.registerOnSharedPreferenceChangeListener(new OnSharedPreferenceChangeListener () {
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
msg (" ***** Shared Preference Update ***** ");
Intent i = new Intent();
i.putExtra("KEY", key);
i.setAction("com.gtosoft.dash.settingschanged");
sendBroadcast(i);
// TODO: fire off the event
}
});
}
The OnSharedPreferenceChangeListener gets garbage collected in your case if you use an anonymous class.
To solve that problem use the following code in PreferenceActivity to register and unregister a change listener:
public class MyActivity extends PreferenceActivity implements
OnSharedPreferenceChangeListener {
#Override
protected void onResume() {
super.onResume();
// Set up a listener whenever a key changes
getPreferenceScreen().getSharedPreferences()
.registerOnSharedPreferenceChangeListener(this);
}
#Override
protected void onPause() {
super.onPause();
// Unregister the listener whenever a key changes
getPreferenceScreen().getSharedPreferences()
.unregisterOnSharedPreferenceChangeListener(this);
}
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,String key)
{
// do stuff
}
Furthermore be aware that the listener only gets called if the actual value changes. Setting the same value again will not fire the listener.
see also SharedPreferences.onSharedPreferenceChangeListener not being called consistently
This happen because garbage collector. its works only one time. then the reference is collected as garbage. so create instance field for listener.
private OnSharedPreferenceChangeListener listner;
listner = new SharedPreferences.OnSharedPreferenceChangeListener() {
#Override
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
//implementation goes here
}
};
prefs.registerOnSharedPreferenceChangeListener(listner);
I arrived here, like many others, because my listener won't be fired when I changed my boolean from true to false, or viceversa.
After much reading, and refactoring, switching contexts/inner classes/privates/static/ and the like, I realized my (stupid) error:
The onSharedPreferenceChanged is only called if something changes. Only. Ever.
During my tests, I was so dumb to click on the same button all the time, thus assigning the same boolean value to the preference all the time, so it did not ever change.
Hope this helps somebody!!
One other way of avoiding the problem is to make your activity the listener class. Since there is only one override method with a distinctive name you can do this:
public class MainActivity extends AppCompatActivity implements SharedPreferences.OnSharedPreferenceChangeListener
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sharedPreferences.registerOnSharedPreferenceChangeListener(this);
...
}
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key)
{
...
}
}
Note the original question spoke of a MainActivity listening to setting changes in a PreferenceActivity. The asker then added an "EDIT1" and changed the question to listening in the PreferenceActivity itself. That is easier than the former and seems to be what all the answers assume. But what if you still want the former scenario?
Well, it will work too, but do not use OnResume() and OnPause() to register and unregister the listener. Doing so will cause the listener to be ineffectual because the user leaves the MainActivity when they use the PreferenceActivity (which makes sense when you think about it). So it will work, but then your MainActivity will still be listening in the background even when the user is not using it. Kind of a waste of resources isn't it? So there is another solution that seems to work, simply add a method to OnResume() to re-read all preferences. That way when a user finishes editing preferences in a PreferenceActivity, the MainActivity will pick them up when the user returns to it and you don't need a listener at all.
Someone please let me know if they see a problem with this approach.
Why don't you just add a onSharedPreferenceChanged in the rest of the activities where the preferences could change?
The garbage collector erases that... you should consider using an Application context instead...or just add the code when app launchs... and then add the the listener with application context...
Consider keeping PreferencesChangeListener inside Android App class instance. Although it's NOT a clean solution storing reference inside App should stop GC from garbage collecting your listener and you should still be able to receive DB change updates. Remember that preference manager does not store a strong reference to the listener! (WeakHashMap)
/**
* Main application class
*/
class MyApp : Application(), KoinComponent {
var preferenceManager: SharedPreferences? = null
var prefChangeListener: MySharedPrefChangeListener? = null
override fun onCreate() {
super.onCreate()
preferenceManager = PreferenceManager.getDefaultSharedPreferences(this)
prefChangeListener = MySharedPrefChangeListener()
preferenceManager?.registerOnSharedPreferenceChangeListener(prefChangeListener)
}
}
and
class MySharedPrefChangeListener : SharedPreferences.OnSharedPreferenceChangeListener {
/**
* Called when a shared preference is changed, added, or removed.
*/
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
if (sharedPreferences == null)
return
if (sharedPreferences.contains(key)) {
// action to perform
}
}
}
While reading Word readable data shared by first app,we should
Replace
getSharedPreferences("PREF_NAME", Context.MODE_PRIVATE);
with
getSharedPreferences("PREF_NAME", Context.MODE_MULTI_PROCESS);
in second app to get updated value in second app.
I have a PreferenceActivity with a bunch of (Sub)PreferenceScreens. Each such (Sub)PreferenceScreen represents an account and has the account-username as its title.
PreferenceScreen root = mgr.createPreferenceScreen(this);
for (MyAccountClass account : myAccounts) {
final PreferenceScreen accScreen = mgr.createPreferenceScreen(this);
accScreen.setTitle(account.getUsername());
// add Preferences to the accScreen
// (for instance a "change username"-preference)
...
root.add(accScreen);
}
As the user enters sub-PreferenceScreen, and edits the account user-name, I want the outer PreferenceScreen to update it's PreferenceScreen-title for the account in question.
I've tried to add...
usernamePref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
accScreen.setTitle(newValue.toString());
return true;
}
});
...but the accScreen.setTitle does not seem to take effect on the outer PreferenceScreen. I've note that calling onContentChanged(); actually makes it work, but I realize that this is probably not the preferred way of doing it.
I suspect I should call postInvalidate() on some view somewhere, but I really can't figure out on what view and when to do it.
PreferenceScreen android:summary update ! may be experiening the same problem as me.
Any help appreciated.
I found a solution to this. I have a hierarchy like this, each of these is a PreferenceScreen:
main settings
-> users list
-> user1 settings
-> user2 settings
...
In the users list, title of the sub-screen is dependent on the user settings. Now when I create the user list, I store the list adapter to a variable in my PreferenceActivity.
PreferenceScreen usersListScreen = ...
userScreenListAdapter = (BaseAdapter)usersListScreen.getRootAdapter();
Now when userX-settings are edited, I set the titles in the usersListScreen and after that call:
userScreenListAdapter.notifyDataSetChanged();
which updates the list UI and the changes are visible.
notifyDataSetChanged() is right solution. But I want to add recursive iterator for all PreferenceScreens, as far as I got a problem to find real parent of a Preference. For complicated structure of Preferences I recommend this killer-code:
private void updateAll_PrefereneScreens(PreferenceGroup group) {
if (group instanceof PreferenceScreen) {
BaseAdapter adapter = (BaseAdapter) ((PreferenceScreen) group).getRootAdapter();
adapter.notifyDataSetChanged();
}
for (int i=0; i<group.getPreferenceCount(); i++) {
Preference pref = group.getPreference(i);
if (pref instanceof PreferenceGroup) {
updateAll_PrefereneScreens((PreferenceGroup) pref);
}
}
}
I call it after every setSummary() to ensure it works properly:
findPreference("KEY").setSummary(str);
updateAll_PrefereneScreens(getPreferenceScreen());
this might be a late answer but still... I'm on it right now :)
The way I achieved it, is that you can hook into the PreferenceFragmentCompat's onResume() method of its lifecycle and manually update the required field by resetting their values.
Note : in this example I also keep track of the index of the edited Preference, just to avoid reset every single ones.
// let's say you need to update title of the parent screen
// when back from sub-screen(s) edition
private int edited = -1;
// this gets called everytime you get back to the parent screen
#Override
public void onResume ()
{
super.onResume();
if ( edited != -1 )
{
PreferenceScreen root = getPreferenceScreen();
Preference preference = root.getPreference( edited );
if ( preference != null )
{
String updatedValue = getPreferenceManager()
.getSharedPreferences()
.getString( "your-preference-key", "your-default-value" );
preference.setTitle( updatedValue );
}
edited = -1;
}
}
// everytime you are about to navigate to a sub-screen
#Override
public boolean onPreferenceTreeClick ( Preference preference )
{
// beware to save it first
if ( preference instanceof MySubScreenPreference )
{
edited = preference.getOrder();
}
return super.onPreferenceTreeClick( preference );
}
As specified in the docs :
onResume
Called when the fragment is visible to the user and actively running. This is generally tied to Activity.onResume of the containing Activity's lifecycle.
Which is good is that it also works of course with FragmentManager's Transactions.
Hope this helps, happy coding ! :)
Experiencing this same problem, but onContentChanged() isn't working for me. My problem is with PreferenceScreens that are more than one level deep from the root.
To follow your example, if you first created an "Accounts" PreferenceScreen, and then added each of your individual account PreferenceScreen objects under that. Like this:
Root Screen
-> "Accounts" screen
-> "foo#example.com" screen
-> edit username
-> edit password
-> etc...
-> "bar#example.com" screen
-> "baz#example.com" screen
-> etc...
If a user edited their username and clicked save, calling PreferenceActivity.onContentChanged() seems to only affect direct descendants of the root PreferenceScreen. The third-generation screens' titles and summaries do not get redrawn, still reflecting old values.
Looking through the code for onContentChanged(), it looks like it just re-bind()s the root Screen to the ListActivity's ListView, although I don't think subsequent PreferenceScreens are ever bound to a ListView (are they?), so we can't manually re-bind anything...
The only workaround I can think of would be to create the sub-menus as isolated PreferenceActivitys instead of PreferenceScreens, so we can intentionally call onContentChanged() on our direct ancestor. But that's even more of a kludge than the current workaround. Any ideas?
PreferenceActivity#onContentChanged() will refresh the whole screen, occurring some flicker effect.
However you can achieve the same goal selectively on a given preference with the PreferenceActivity#onPreferenceTreeClick(...) method.
Note that this method is now deprecated : consider using fragments that seems to solve a lot of issues with custom preferences (I've not tested myself yet). There is a compatibility package for older SDK.
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
Log.v(TAG, "onSharedPreferenceChanged(...," + key + ")");
if (key.equals("myPref")) {
onPreferenceTreeClick(getPreferenceScreen(), getPreferenceManager().findPreference("myPref"));
Log.v(TAG, "Do whatever else you need...");
}
//onContentChanged(); // this could be used but occurs screen flickering
}
I'm just putting
((BaseAdapter)getPreferenceScreen().getRootAdapter()).notifyDataSetChanged();
right after updating the summary of my parent preference item.
Source
// Import required classes (Win: CTRL+SHIFT+O & Mac: CMD+SHIFT+O)
public class YourCustomPreference extends PreferenceActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.preferences);
}
// some logic goes above, when you want to reset value and update
// EditTextPreference value. For convenience, I am going to wrap two
// different task in different methods
private void resetPreferenceValue() {
SharedPreferences sharedPref = PreferenceManager
.getDefaultSharedPreferences(this.getApplicationContext());
// Get preference in editor mode
SharedPreferences.Editor prefEditor = sharedPref.edit();
// set your default value here (could be empty as well)
prefEditor.putString("your_edit_text_pref_key", "DEFAULT-VALUE");
prefEditor.commit(); // finally save changes
// Now we have updated shared preference value, but in activity it
// still hold the old value
this.resetElementValue();
}
private void resetElementValue() {
// First get reference to edit-text view elements
EditTextPreference myPrefText = (EditTextPreference) super
.findPreference("your_edit_text_pref_key");
// Now, manually update it's value to default/empty
myPrefText.setText("DEFAULT-VALUE");
// Now, if you click on the item, you'll see the value you've just
// set here
}
}
This works for me, you have to grab the underlying dialog of your PreferenceScreen and set the title from there, really easy.
somePrefScreen.getDialog().setTitle("Whatever you want");
I'm registering a preference change listener like this (in the onCreate() of my main activity):
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
prefs.registerOnSharedPreferenceChangeListener(
new SharedPreferences.OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(
SharedPreferences prefs, String key) {
System.out.println(key);
}
});
The trouble is, the listener is not always called. It works for the first few times a preference is changed, and then it is no longer called until I uninstall and reinstall the app. No amount of restarting the application seems to fix it.
I found a mailing list thread reporting the same problem, but no one really answered him. What am I doing wrong?
This is a sneaky one. SharedPreferences keeps listeners in a WeakHashMap. This means that you cannot use an anonymous inner class as a listener, as it will become the target of garbage collection as soon as you leave the current scope. It will work at first, but eventually, will get garbage collected, removed from the WeakHashMap and stop working.
Keep a reference to the listener in a field of your class and you will be OK, provided your class instance is not destroyed.
i.e. instead of:
prefs.registerOnSharedPreferenceChangeListener(
new SharedPreferences.OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
// Implementation
}
});
do this:
// Use instance field for listener
// It will not be gc'd as long as this instance is kept referenced
listener = new SharedPreferences.OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
// Implementation
}
};
prefs.registerOnSharedPreferenceChangeListener(listener);
The reason unregistering in the onDestroy method fixes the problem is because to do that you had to save the listener in a field, therefore preventing the issue. It's the saving the listener in a field that fixes the problem, not the unregistering in onDestroy.
UPDATE: The Android docs have been updated with warnings about this behavior. So, oddball behavior remains. But now it's documented.
this accepted answer is ok, as for me it is creating new instance each time the activity resumes
so how about keeping the reference to the listener within the activity
OnSharedPreferenceChangeListener listener = new OnSharedPreferenceChangeListener(){
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
// your stuff
}
};
and in your onResume and onPause
#Override
public void onResume() {
super.onResume();
getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(listener);
}
#Override
public void onPause() {
super.onPause();
getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(listener);
}
this will very similar to what you are doing except we are maintaining a hard reference.
The accepted answer creates a SharedPreferenceChangeListener every time onResume is called. #Samuel solves it by making SharedPreferenceListener a member of the Activity class. But there's a third and a more straightforward solution that Google also uses in this codelab. Make your activity class implement the OnSharedPreferenceChangeListener interface and override onSharedPreferenceChanged in the Activity, effectively making the Activity itself a SharedPreferenceListener.
public class MainActivity extends Activity implements SharedPreferences.OnSharedPreferenceChangeListener {
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
}
#Override
protected void onStart() {
super.onStart();
PreferenceManager.getDefaultSharedPreferences(this)
.registerOnSharedPreferenceChangeListener(this);
}
#Override
protected void onStop() {
super.onStop();
PreferenceManager.getDefaultSharedPreferences(this)
.unregisterOnSharedPreferenceChangeListener(this);
}
}
As this is the most detailed page for the topic I want to add my 50ct.
I had the problem that OnSharedPreferenceChangeListener wasn't called. My SharedPreferences are retrieved at the start of the main Activity by:
prefs = PreferenceManager.getDefaultSharedPreferences(this);
My PreferenceActivity code is short and does nothing except showing the preferences:
public class Preferences extends PreferenceActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// load the XML preferences file
addPreferencesFromResource(R.xml.preferences);
}
}
Every time the menu button is pressed I create the PreferenceActivity from the main Activity:
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
//start Preference activity to show preferences on screen
startActivity(new Intent(this, Preferences.class));
//hook into sharedPreferences. THIS NEEDS TO BE DONE AFTER CREATING THE ACTIVITY!!!
prefs.registerOnSharedPreferenceChangeListener(this);
return false;
}
Note that registering the OnSharedPreferenceChangeListener needs to be done AFTER creating the PreferenceActivity in this case, else the Handler in the main Activity won't be called!!! It took me some sweet time to realize that...
Kotlin Code for register SharedPreferenceChangeListener it detect when change will happening on the saved key :
PreferenceManager.getDefaultSharedPreferences(this)
.registerOnSharedPreferenceChangeListener { sharedPreferences, key ->
if(key=="language") {
//Do Something
}
}
you can put this code in onStart() , or somewhere else..
*Consider that you must use
if(key=="YourKey")
or your codes inside "//Do Something " block will be run wrongly for every change that will happening in any other key in sharedPreferences
So, I don't know if this would really help anyone though, it solved my issue.
Even though I had implemented the OnSharedPreferenceChangeListener as stated by the accepted answer. Still, I had an inconsistency with the listener being called.
I came here to understand that the Android just sends it for garbage collection after some time. So, I looked over at my code.
To my shame, I had not declared the listener GLOBALLY but instead inside the onCreateView. And that was because I listened to the Android Studio telling me to convert the listener to a local variable.
It make sense that the listeners are kept in WeakHashMap.Because most of the time, developers prefer to writing the code like this.
PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).registerOnSharedPreferenceChangeListener(
new OnSharedPreferenceChangeListener() {
#Override
public void onSharedPreferenceChanged(
SharedPreferences sharedPreferences, String key) {
Log.i(LOGTAG, "testOnSharedPreferenceChangedWrong key =" + key);
}
});
This may seem not bad. But if the OnSharedPreferenceChangeListeners' container was not WeakHashMap, it would be very bad.If the above code was written in an Activity . Since you are using non-static (anonymous) inner class which will implicitly holds the reference of the enclosing instance. This will cause memory leak.
What's more, If you keep the listener as a field, you could use registerOnSharedPreferenceChangeListener at the start and call unregisterOnSharedPreferenceChangeListener in the end. But you can not access a local variable in a method out of it's scope. So you just have the opportunity to register but no chance to unregister the listener. Thus using WeakHashMap will resolve the problem. This is the way I recommend.
If you make the listener instance as a static field, It will avoid the memory leak caused by non-static inner class. But as the listeners could be multiple, It should be instance-related. This will reduce the cost of handling the onSharedPreferenceChanged callback.
While reading Word readable data shared by first app,we should
Replace
getSharedPreferences("PREF_NAME", Context.MODE_PRIVATE);
with
getSharedPreferences("PREF_NAME", Context.MODE_MULTI_PROCESS);
in second app to get updated value in second app.
But still it is not working...