【问题标题】:SharedPreferences - Activity and BroadcastReceiverSharedPreferences - 活动和广播接收器
【发布时间】:2016-04-26 17:37:44
【问题描述】:

我目前正在使用 SharedPreferences 来跟踪要在通过 AlarmManager 启动的 BroadcastReceiver 中执行工作的项目列表。除了特定场景外,一切都很好。当我触发一个新项目来执行工作时,让它完成工作,然后删除该项目(全部通过 SharedPreferences 编辑),它在应用程序运行时运行良好。当列表中没有任何内容并且我打开任务管理器并终止应用程序时,该项目突然出现在 BroadcastReceiver 中(在应用程序关闭后仍在运行)。是什么导致了这种行为?我应该在应用程序退出时杀死所有接收者吗?当 Receiver 仍在运行时,Activity 关闭是否默认返回到不同的 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 启动
  • 声明中没有特殊设置
  • 卸载应用程序后可重复此操作,清除接收器中的所有首选项(以防它使用不同的首选项)

【问题讨论】:

  • 这似乎与使用 StringSet 直接相关,尽管我还没有确定原因。选择手动构建和解析字符串而不是使用字符串集。

标签: android broadcastreceiver sharedpreferences alarmmanager


【解决方案1】:

editor.apply(); 更改为editor.commit()。当您终止应用程序时,更改可能不会写入磁盘。来自http://developer.android.com/reference/android/content/SharedPreferences.Editor.html#apply()的官方文档

与commit() 将其首选项同步写入持久存储不同,apply() 立即将其更改提交到内存中的SharedPreferences,但开始异步提交到磁盘,并且您不会收到任何失败的通知。如果此 SharedPreferences 上的另一个编辑器在 apply() 仍未完成时执行常规 commit(),则 commit() 将阻塞,直到所有异步提交以及提交本身都完成。

【讨论】:

  • 感谢您的回复。我最初使用 commit 并切换到 apply 以尝试解决问题。两者的工作方式相同。结果是 StringSet 功能不理解对其内容的更改,因此即使您编辑数据,它似乎与 sharedprefs 相同。
【解决方案2】:

如果您在使用 StringSet 时遇到任何细微差别,解决方案是将另一个属性与 StringSet 本身一起写入 SharedPreferences 对象(如 StringSet.size())。原因是 SharedPreferences 库只是将对象与存储的对象进行比较,添加/删除数据并不一定会导致对象本身发生变化,因此看起来好像没有差异。

可以检查对象的大小,如果在编辑它时为 0,而不是编辑它,只需在将对象保存到 SharedPreferences 之前将其设置为 null。我选择了第二个 sharedpref 设置,从那以后它运行良好。

【讨论】:

    【解决方案3】:

    尽管这是一个相对较旧的问题,但我偶然发现了同样的问题。 Bu 供将来参考,这可能很有用。

    问题:从 BroadcastReceiver 检索的旧值。这是由 SharedPreferences 不更新 StringSet 的内容引起的,因为它是同一个对象。

    更改了以下内容以通过 SharedPreferences 提供持久存储(我不知道哪些内容有助于解决方案):

    • 在清单中我有 android:process=":remote",必须将其删除。
    • 我在 sharedpreferences 中使用 Context.MODE_MULTI_PROCESS 作为模式 (context.getSharedPreferences(ref,Context.MODE_MULTI_PROCESS))
    • 最后(因为this 发布),首先提交null 值解决了我的问题:
        editor.putStringSet(ref,null);
        editor.commit();
        editor.putStringSet(ref, valuesToBeStored);
    

    我不知道是否需要前两个更改。

    【讨论】:

    • 这并没有提供问题的答案。一旦你有足够的reputation,你就可以comment on any post;相反,provide answers that don't require clarification from the asker。 - From Review
    • 这个答案与使用 StringSet 时的问题有关。这个答案为“当列表中没有任何内容并且我打开任务管理器并终止应用程序时,该项目突然出现在 BroadcastReceiver 中”的问题提供了解决方案。程序的预期行为应该是最后保存的值应该是检索到的值。
    • 我已经编辑了我的答案,因此与问题相关的问题得到了更好的回答。
    猜你喜欢
    • 2017-11-02
    • 2011-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多