SharedPreferences - активност и BroadcastReceiver

В момента използвам SharedPreferences, за да следя списък с елементи, върху които да извърша работа в BroadcastReceiver, стартиран чрез AlarmManager. Всичко работи чудесно, с изключение на определен сценарий. Когато задействам нов елемент, върху който да извърша работа, оставям го да свърши работата, след което премахвам този елемент (всички чрез редакции на SharedPreferences), той работи прекрасно, докато приложението работи. Когато няма нищо в списъка и отворя диспечера на задачите и затворя приложението, изведнъж елементът се появява обратно в BroadcastReceiver (който все още работи, след като приложението се затвори). Какво причинява това поведение? Трябва ли просто да убия всички приемници при излизане от приложението? Затварянето на дейността връща ли се по подразбиране към различен обект SharedPreferences, когато приемникът все още работи?

Код за добавяне/премахване на елементи от обекта 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 направи обикновен commit(), докато apply() все още не е изпълнен, commit() ще блокира, докато не бъдат завършени всички асинхронни ангажименти, както и самият комит.

person Hoan Nguyen    schedule 01.05.2013
comment
Благодаря за отговора. Първоначално бях използвал commit и превключих на apply, за да се опитам да разреша проблема. И двете работиха по същия начин. Оказва се, че функционалността StringSet не разбира промените в съдържанието си, така че дори когато редактирате данните, те изглеждат същите за sharedprefs. - person methodin; 01.05.2013

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

Възможно е да проверите размера на обекта и ако той е 0, когато го редактирате, вместо да го редактирате, просто задайте обекта на нула, преди да го запишете в 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
Това не дава отговор на въпроса. След като имате достатъчна репутация, ще можете да коментирайте всяка публикация; вместо това предоставете отговори които не изискват пояснение от питащия. - От преглед - person Laur Ivan; 26.04.2016
comment
Този отговор се отнася до проблема при използване на StringSet. Този отговор дава решение на проблема „Когато в списъка няма нищо и отворя диспечера на задачите и затворя приложението, изведнъж елементът се появява обратно в BroadcastReceiver“. Очакваното поведение на програмата трябва да бъде, че последната запазена стойност трябва да бъде извлечената стойност. - person gillesC; 26.04.2016
comment
Редактирах отговора си, така че проблемът, свързан с въпроса, да има по-добър отговор. - person gillesC; 26.04.2016