SharedPreferences — активность и BroadcastReceiver

В настоящее время я использую SharedPreferences для отслеживания списка элементов для выполнения работы в BroadcastReceiver, запущенном через AlarmManager. Все работает отлично, кроме определенного сценария. Когда я запускаю новый элемент для выполнения работы, позволяю ему выполнять работу, а затем удаляю этот элемент (все через правки SharedPreferences), он прекрасно работает во время работы приложения. Когда в списке ничего нет, и я открываю диспетчер задач и закрываю приложение, внезапно элемент снова появляется в BroadcastReceiver (который все еще работает после закрытия приложения). Что вызывает такое поведение? Должен ли я просто убить всех получателей при выходе из приложения? Возвращается ли закрытие Activity по умолчанию к другому объекту SharedPreferences, когда Receiver все еще работает?

Код для добавления/удаления элементов из объекта SharedPreferences

final SharedPreferences prefs = context.getSharedPreferences(Config.PREFS_NAME,
                Context.MODE_PRIVATE);
final Editor editor = prefs.edit();
mUpdates = prefs.getStringSet(Config.PREFS_KEY_ACTIVE_TASKS, new HashSet<String>());

if (!mUpdates.contains(key)) {
    mUpdates.add(key);
} else {
    mUpdates.remove(key);
}
editor.putStringSet(Config.PREFS_KEY_ACTIVE_TASKS, mUpdates);
editor.apply();

Код широковещательного приемника

public void onReceive(Context context, Intent intent) {
    SharedPreferences prefs = context.getSharedPreferences(Config.PREFS_NAME, Context.MODE_PRIVATE);
    if(prefs.contains(Config.PREFS_KEY_ACTIVE_TASKS)) {
        Set<String> updates = prefs.getStringSet(Config.PREFS_KEY_ACTIVE_TASKS, null);
        if(updates != null) {
            Log.d("RECEIVER","Size="+updates.size());
            for(String key : updates) {
                EntityChangeManager.notifyListeners(key);
            }
        }
    }
}

Когда я запускаю код для добавления/удаления объектов из исходного списка, как и ожидалось, я вижу

04-30 20:04:44.165: D/RECEIVER(27079): Size=1
04-30 20:04:44.165: D/RECEIVER(27079): Size=0

Когда я убиваю приложение, я вижу

04-30 20:04:43.244: D/ActivityThread(27079): setTargetHeapUtilization:0.25
04-30 20:04:43.244: D/ActivityThread(27079): setTargetHeapIdealFree:8388608
04-30 20:04:43.254: D/ActivityThread(27079): setTargetHeapConcurrentStart:2097152
04-30 20:04:43.264: D/RECEIVER(27079): Size=1

Точки интереса:

  • Приемник работает каждую секунду
  • Приемник запускается из AlarmManager
  • Никаких специальных настроек в декларации
  • Это повторяется после удаления приложения, очистки всех настроек в приемнике (если он использовал другой)

person methodin    schedule 01.05.2013    source источник
comment
Похоже, это напрямую связано с использованием StringSet, хотя я не определил причину. Выбрана ручная сборка и анализ строки вместо использования набора строк.   -  person methodin    schedule 01.05.2013


Ответы (3)


Измените editor.apply(); на editor.commit(). Изменение не может быть записано на диск при закрытии приложения. Из официального документа на http://developer.android.com/reference/android/content/SharedPreferences.Editor.html#apply()

В отличие от commit(), который синхронно записывает свои предпочтения в постоянное хранилище, apply() немедленно фиксирует свои изменения в SharedPreferences в памяти, но запускает асинхронную фиксацию на диск, и вы не будете уведомлены о каких-либо сбоях. Если другой редактор в этом SharedPreferences выполняет обычную фиксацию (), в то время как применение () все еще не выполнено, фиксация () будет блокироваться до тех пор, пока не будут завершены все асинхронные фиксации, а также сама фиксация.

person Hoan Nguyen    schedule 01.05.2013
comment
Спасибо за ответ. Первоначально я использовал фиксацию и переключился на применение, чтобы попытаться решить проблему. Оба работали одинаково. Оказывается, функциональность StringSet не понимает изменения своего содержимого, поэтому даже когда вы редактируете данные, они выглядят так же, как и общие настройки. - person methodin; 01.05.2013

Если вы сталкиваетесь с какими-либо нюансами при использовании StringSet, решение состоит в том, чтобы записать еще один атрибут в объект SharedPreferences (например, StringSet.size()) в тандеме с самим StringSet. Причина в том, что библиотека SharedPreferences только сравнивает объект с сохраненным объектом, а добавление/удаление данных не обязательно приводит к изменению самого объекта, поэтому кажется, что различий нет.

Можно проверить размер объекта, и если он равен 0, когда вы редактируете его, вместо того, чтобы редактировать его, просто установите для объекта значение null, прежде чем сохранять его в SharedPreferences. Я выбрал второй параметр sharedpref, и с тех пор он работает нормально.

person methodin    schedule 01.05.2013

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

Проблема: старые значения извлекаются из BroadcastReceiver. Это вызвано SharedPreferences, который не обновляет содержимое StringSet, потому что это один и тот же объект.

Следующие вещи были изменены, чтобы обеспечить постоянное хранилище с помощью SharedPreferences (я не знаю, какие вещи способствовали решению):

  • В манифесте у меня было android:process=":remote", которое пришлось удалить.
  • Я использовал Context.MODE_MULTI_PROCESS в качестве режима в общих настройках (context.getSharedPreferences(ref,Context.MODE_MULTI_PROCESS))
  • И, наконец (из-за этого поста), первая фиксация значения null решила мою проблему:
    editor.putStringSet(ref,null);
    editor.commit();
    editor.putStringSet(ref, valuesToBeStored);

Я не знаю, были ли необходимы первые два изменения.

person gillesC    schedule 25.04.2016
comment
Это не дает ответа на вопрос. Получив достаточную репутацию, вы сможете /comment">прокомментировать любой пост; вместо этого дайте ответы которые не требуют разъяснений от спрашивающего. – Из обзора - person Laur Ivan; 26.04.2016
comment
Этот ответ относится к проблеме при использовании StringSet. Этот ответ дает решение проблемы «Когда в списке ничего нет, и я открываю диспетчер задач и закрываю приложение, внезапно элемент снова появляется в BroadcastReceiver». Ожидаемое поведение программы должно заключаться в том, что последнее сохраненное значение должно быть полученным значением. - person gillesC; 26.04.2016
comment
Я отредактировал свой ответ, чтобы лучше ответить на проблему, связанную с вопросом. - person gillesC; 26.04.2016