14

I have a Activity which updates a string in the SharedPreferences.

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

I then start a service:

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

The service creates a reference to Alarm which extends BroadcastReceiver:

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

In SetAlarm I do all the basic setting up stuff (At this point, "username" is still correct.. i checked):

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

I then stop the service and then start it again (using 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);
}

The issue is in onReceive.. the first time the "username" field is correct. The second time, if username is updated between the service stopping and starting, however, it returns the first value. The value does not seem to get updated...

public void onReceive(Context context, Intent intent) {   
    SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
    Log.e("hi", settings.getString("username", ""));
}
spalt
  • 193
  • 1
  • 9
  • How do you change the username? Ensure that you are saving the correct username before you put it to shared pref – San Apr 11 '12 at 02:31
  • Sadly, I'm just doing the typical putString then editor.commit(). The funny thing is if I do a getString it pulls the updated username all the way until the last step, onReceive.. – spalt Apr 11 '12 at 12:44

2 Answers2

68

I had the same problem and after struggling for hours to solve it, I finally found the issue causing it. In your AndroidManifest you probably have something like that:

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

The last attribute (process:remote) cause the receiver to run on a different/new process when it is called. But SharedPreferences is NOT supported between different processes.

So what I did is to remove that last attribute from the manifest. The implication is that the code will now run on the main thread - but if you only have a few lines to show a notification then that shouldn't be a problem. Another way is to call a service to run the long operation.

Amir Naor
  • 2,236
  • 20
  • 27
  • 1
    thanks man! I eventually ended up solving it by using ActivityManager.killBackgroundProcess to stop the service in this situation. When it starts again, it gets the new data properly. Downside is it requires a new user permission, but it works. – spalt Jun 25 '12 at 19:28
  • Remember this is also true if another process calls this receiver too! My service was the culprit using the separate process, and was registering my receiver. Thanks though! Like a lot of people here, I spent ages debugging my SharedPreferences receiving old data in the receiver... – MrDoughnut Nov 20 '15 at 14:23
  • another way is if you want to keep :remote then save settings using Context.MODE_WORLD_READABLE so that it is accessible from outside also – Abhijit Nov 26 '16 at 11:18
2

Unfortunately the solution by Amir Naor not worked in my Android 7 App. It seems that every receiver get always started in a new process.

API < 23

So if your app < API Level 23, you can use the flag Context.MODE_MULTI_PROCESS:

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

API >= 23

What a surprise. Since API Level 23 is the flag Context.MODE_MULTI_PROCESS deprecated and we should use ContentProvider to share the Properties between processes.


There is a very nice library on github: Tray - a SharedPreferences replacement for Android. This library is a ContentProvider wrapper with some other useful features. Give it a try.

alex
  • 8,904
  • 6
  • 49
  • 75