OnSharedPreferenceChanged не активируется, если изменения происходят в отдельной активности?

Я реализовал onSharedPreferenceChanged в своей основной деятельности.

Если я изменю настройки в основной активности, мое событие срабатывает.

Если я изменил настройки на экране моих настроек (PreferenceActivity), мое событие НЕ запускается при изменении настроек (поскольку это отдельное действие и отдельная ссылка на sharedPreferences?)

Есть ли у кого-нибудь рекомендация о том, как мне следует преодолевать эту ситуацию?

Спасибо!

EDIT1: Я попытался добавить обработчик событий прямо в свою активность, но он никогда не срабатывает. Следующий метод вызывается во время onCreate моей активности предпочтений. Когда я изменяю значения, он никогда не печатает сообщение (msg() является оберткой для 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
        }
    });
}

Ответ 1

OnSharedPreferenceChangeListener получает мусор, собранный в вашем случае, если вы используете анонимный класс.

Чтобы решить эту проблему, используйте следующий код в PreferenceActivity для регистрации и отмены регистрации прослушивателя изменений:

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
}

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

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

Ответ 2

Это происходит потому, что сборщик мусора. его работы только один раз. тогда ссылка собирается как мусор. поэтому создайте поле экземпляра для слушателя.

private OnSharedPreferenceChangeListener listner;

listner = new SharedPreferences.OnSharedPreferenceChangeListener() {        
        @Override
        public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
            //implementation goes here
        }
    };
    prefs.registerOnSharedPreferenceChangeListener(listner);

Ответ 3

Я приехал сюда, как и многие другие, потому что мой слушатель не будет запущен, когда я изменил свое логическое значение с true на false или наоборот.

После многократного чтения и рефакторинга, переключение contexts/inner classes/privates/static/ и т.п., я понял свою (тупую) ошибку:

onSharedPreferenceChanged называется только, если что-то меняется. Только. Когда-нибудь.

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

Надеюсь, это поможет кому-то!

Ответ 4

Еще один способ избежать проблемы - сделать вашу деятельность классом слушателя. Поскольку существует только один метод переопределения с отличительным именем, вы можете сделать это:

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

Ответ 5

Обратите внимание, что в исходном вопросе говорилось о MainActivity, который прослушивал изменения настроек в PreferenceActivity. Затем искатель добавил "EDIT1" и изменил вопрос на прослушивание в самой PreferenceActivity. Это проще, чем первое, и, похоже, это то, что все ответы предполагают. Но что, если вы все еще хотите использовать прежний сценарий?

Ну, это тоже сработает, но не используйте OnResume() и OnPause() для регистрации и отмены регистрации слушателя. Это приведет к тому, что слушатель окажется неэффективным, потому что пользователь покидает MainActivity, когда использует PreferenceActivity (что имеет смысл, когда вы об этом думаете). Так что это сработает, но ваша MainActivity все равно будет прослушиваться в фоновом режиме, даже если пользователь не использует его. Вид траты ресурсов не так ли? Итак, есть другое решение, которое, похоже, работает, просто добавьте метод OnResume(), чтобы перечитать все настройки. Таким образом, когда пользователь заканчивает редактирование предпочтений в PreferenceActivity, MainActivity выберет их, когда пользователь вернется к нему, и вам вообще не нужен прослушиватель.

Кто-нибудь, пожалуйста, сообщите мне, если они видят проблему с этим подходом.

Ответ 6

Почему бы вам просто не добавить onSharedPreferenceChanged в остальные действия, в которых могут измениться предпочтения?

Ответ 7

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

Ответ 8

Подумайте о том, чтобы сохранить PreferencesChangeListener внутри экземпляра класса Android App. Хотя это НЕ чистое решение для хранения ссылок внутри приложения, оно должно остановить сборщик мусора GC, и вы все равно сможете получать обновления изменений БД. Помните, что менеджер предпочтений не хранит сильную ссылку на слушателя! (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)
    }
}

а также

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

Ответ 9

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

Заменить

getSharedPreferences("PREF_NAME", Context.MODE_PRIVATE);

с

getSharedPreferences("PREF_NAME", Context.MODE_MULTI_PROCESS);

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