广播接收器中的SharedPreferences似乎无法更新?

14

我有一个活动(Activity),它会更新SharedPreferences中的一个字符串。

SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor editor = settings.edit();
editor.putString("username", username);
editor.commit();

我随后启动了一个服务:

startService(new Intent(this, MyService.class));

该服务创建了一个对扩展了BroadcastReceiver的Alarm的引用:

Alarm alarm = null;
public void onCreate() {
    alarm = new Alarm();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    alarm.SetAlarm(this);
}

在SetAlarm函数里,我完成了所有基本的设置工作(此时,“username”还是正确的..我已经检查过了):

public void SetAlarm(Context context) {
    AlarmManager am=(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
    PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0);
    am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), 1000 * 60 * interval, pi);
}

我停止了服务,然后再次启动它(使用SetAlarm)。

public void CancelAlarm(Context context) {
   Intent intent = new Intent(context, Alarm.class);
   PendingIntent sender = PendingIntent.getBroadcast(context, 0, intent, 0);
   AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
   alarmManager.cancel(sender);
}
问题出在onReceive函数中。第一次,“username”字段是正确的。然而,如果在服务停止和重新启动之间更新了用户名,则第二次它将返回第一个值。该值似乎没有更新...
public void onReceive(Context context, Intent intent) {   
    SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
    Log.e("hi", settings.getString("username", ""));
}

你怎么修改用户名?确保在将其保存到共享首选项之前,你已经保存了正确的用户名。 - San
遗憾的是,我只是做了典型的putString然后editor.commit()。有趣的是,如果我执行getString,它会一直提取更新后的用户名,直到最后一步onReceive... - spalt
2个回答

68

我曾经遇到过同样的问题,花了几个小时才终于找到了导致问题的原因。在你的AndroidManifest文件中,可能有以下类似的内容:

<receiver android:name="AlarmReceiver" android:process=":remote" />

最后一个属性(process:remote)会在调用时使接收器在不同/新进程上运行。但是SharedPreferences不支持在不同的进程之间使用。

所以我做的是从清单中删除了该属性。这意味着代码现在将在主线程上运行——但如果您只需要显示几行通知,那就不应该是问题。另一种方法是调用服务来运行长时间的操作。


1
谢谢你!最终我通过使用ActivityManager.killBackgroundProcess来停止服务从而解决了这个问题。 当它再次启动时,可以正确获取新数据。 缺点是需要一个新的用户权限,但它可行。 - spalt
请记住,如果另一个进程也调用了此接收器,则此语句同样适用!我的服务是罪魁祸首,使用单独的进程,并注册了我的接收器。谢谢!和这里的很多人一样,我花费了很长时间来调试我的SharedPreferences,在接收器中接收旧数据... - MrDoughnut
另一种方法是,如果你想保持 ": remote",那么请使用 Context.MODE_WORLD_READABLE 保存设置,这样它也可以从外部访问。 - Abhijit

2

很遗憾,Amir Naor提供的解决方案在我的Android 7应用中没有起作用。似乎每个接收器始终在新进程中启动。

API < 23

因此,如果您的应用程序小于API Level 23,则可以使用标志Context.MODE_MULTI_PROCESS

context.getSharedPreferences("mypreferences", Context.MODE_PRIVATE | Context.MODE_MULTI_PROCESS);

API >= 23

令人惊讶的是,自API级别23以来,标志Context.MODE_MULTI_PROCESS弃用,我们应该使用ContentProvider在进程之间共享属性。


Github上有一个非常好的库:Tray - Android的SharedPreferences替代品。这个库是一个带有一些其他有用功能的ContentProvider包装器。试试吧。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接