SharedPreferences.onSharedPreferenceChangeListener не называется последовательно

Я регистрирую прослушиватель предпочтений как это (в onCreate() моего основного действия):

 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); prefs.registerOnSharedPreferenceChangeListener( new SharedPreferences.OnSharedPreferenceChangeListener() { public void onSharedPreferenceChanged( SharedPreferences prefs, String key) { System.out.println(key); } }); 

Беда в том, что слушатель не всегда называется. Он работает в первые несколько раз, когда предпочтение изменяется, а затем оно больше не вызывается до тех пор, пока я не удалю и не переустановить приложение. Никакой перезапуск приложения, похоже, не исправил его.

Я нашел поток списка рассылки, сообщающий о той же проблеме, но на него никто не ответил. Что я делаю не так?

Solutions Collecting From Web of "SharedPreferences.onSharedPreferenceChangeListener не называется последовательно"

Это подлый. SharedPreferences хранит слушателей в файле WeakHashMap. Это означает, что вы не можете использовать анонимный внутренний класс в качестве слушателя, так как он станет целью сбора мусора, как только вы покинете текущую область. Сначала это сработает, но, в конце концов, получит собранный мусор, удалит из WeakHashMap и перестанет работать.

Храните ссылку на слушателя в поле своего класса, и все будет в порядке, если ваш экземпляр класса не будет уничтожен.

Т.е. вместо:

 prefs.registerOnSharedPreferenceChangeListener( new SharedPreferences.OnSharedPreferenceChangeListener() { public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { // Implementation } }); 

сделай это:

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

Причина отказа от регистрации в методе onDestroy устраняет проблему, потому что для этого вам нужно было сохранить слушателя в поле, что предотвратит проблему. Это сохранение слушателя в поле, которое устраняет проблему, а не отмену регистрации в onDestroy.

ОБНОВЛЕНИЕ . Документы Android обновлены с предупреждениями об этом поведении. Таким образом, поведение странного поведения остается. Но теперь это задокументировано.

Поскольку это самая подробная страница для этой темы, я хочу добавить 50ct.

У меня была проблема, что OnSharedPreferenceChangeListener не вызывался. Мои SharedPreferences извлекаются в начале основной операции:

 prefs = PreferenceManager.getDefaultSharedPreferences(this); 

Мой код PreferenceActivity является коротким и ничего не делает, кроме отображения настроек:

 public class Preferences extends PreferenceActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // load the XML preferences file addPreferencesFromResource(R.xml.preferences); } } 

Каждый раз, когда нажимается кнопка меню, я создаю PreferenceActivity из основного действия:

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

Обратите внимание, что регистрация OnSharedPreferenceChangeListener должна быть выполнена ПОСЛЕ создания PreferenceActivity в этом случае, иначе обработчик в основной операции не будет вызываться !!! Мне потребовалось некоторое время, чтобы понять, что …

Этот принятый ответ в порядке, так как для меня он создает новый экземпляр каждый раз, когда активность возобновляется

Так как насчет сохранения ссылки на слушателя в рамках деятельности

 OnSharedPreferenceChangeListener myPrefListner = new OnSharedPreferenceChangeListener(){ public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { // your stuff } }; 

И в вашем onResume и onPause

 @Override protected void onResume() { super.onResume(); getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(myPrefListner); } @Override protected void onPause() { super.onPause(); getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(myPrefListner); } 

Это очень похоже на то, что вы делаете, за исключением того, что мы сохраняем твердую ссылку.

Читая читаемые Word данные, разделяемые первым приложением, мы должны

замещать

 getSharedPreferences("PREF_NAME", Context.MODE_PRIVATE); 

с

 getSharedPreferences("PREF_NAME", Context.MODE_MULTI_PROCESS); 

Во втором приложении, чтобы получить обновленное значение во втором приложении.

Но все же он не работает …

Имеет смысл, что слушатели хранятся в WeakHashMap. Большую часть времени разработчики предпочитают писать такой код.

 PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).registerOnSharedPreferenceChangeListener( new OnSharedPreferenceChangeListener() { @Override public void onSharedPreferenceChanged( SharedPreferences sharedPreferences, String key) { Log.i(LOGTAG, "testOnSharedPreferenceChangedWrong key =" + key); } }); 

Это может показаться неплохим. Но если контейнер OnSharedPreferenceChangeListeners не был WeakHashMap, это было бы очень плохо. Если приведенный выше код был записан в Activity. Поскольку вы используете нестатический (анонимный) внутренний класс, который будет неявно содержать ссылку на экземпляр окружения. Это приведет к утечке памяти.

Более того, если вы сохраняете слушателя как поле, вы можете использовать registerOnSharedPreferenceChangeListener в начале и вызывать unregisterOnSharedPreferenceChangeListener в конце. Но вы не можете получить доступ к локальной переменной в методе из своей области. Таким образом, у вас есть возможность зарегистрироваться, но нет возможности отменить регистрацию слушателя. Таким образом, использование WeakHashMap решит проблему. Так я рекомендую.

Если вы сделаете экземпляр слушателя как статическое поле, он позволит избежать утечки памяти, вызванной нестационарным внутренним классом. Но поскольку слушатели могут быть краткими, это должно быть связано с экземплярами. Это уменьшит стоимость обработки обратного вызова onSharedPreferenceChanged .