onSharedPreferenceChanged not fired if change occurs in separate activity?

AndroidSharedpreferences

Android Problem Overview


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
		}
	});
}

Android Solutions


Solution 1 - Android

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 https://stackoverflow.com/questions/2542938/sharedpreferences-onsharedpreferencechangelistener-not-being-called-consistently/3104265#3104265

Solution 2 - Android

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);

Solution 3 - Android

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!!

Solution 4 - Android

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)
    {
        ...
    }
} 

Solution 5 - Android

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.

Solution 6 - Android

Why don't you just add a onSharedPreferenceChanged in the rest of the activities where the preferences could change?

Solution 7 - Android

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...

Solution 8 - Android

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
        }
    }
}

Solution 9 - Android

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBrad HeinView Question on Stackoverflow
Solution 1 - AndroidthumbmunkeysView Answer on Stackoverflow
Solution 2 - AndroidhderangaView Answer on Stackoverflow
Solution 3 - AndroidmrAriasView Answer on Stackoverflow
Solution 4 - AndroidSteve WaringView Answer on Stackoverflow
Solution 5 - AndroidtransView Answer on Stackoverflow
Solution 6 - AndroidCristianView Answer on Stackoverflow
Solution 7 - AndroidsuperUserView Answer on Stackoverflow
Solution 8 - Androidkosiara - Bartosz KosarzyckiView Answer on Stackoverflow
Solution 9 - AndroidShridutt KothariView Answer on Stackoverflow