11

I use SharedPreferences to write and later read values within different activities in my application. It used to work ok but lately it seems like it if wasn't sincronized. I mean, I write a value but then the other activity still reads the old value. Sometimes it works correcly. Any idea?

EDIT: This is a sample code:

First, from a thread:

SharedPreferences prefs = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putInt("ComandToDo", value);
editor.commit();
... some code later:
alarmmanager.set(AlarmManager.RTC_WAKEUP, Miliseconds, sender);

In the alarm receiver:

SharedPreferences prefs = contexto.getSharedPreferences("MyPrefs", Context.MODE_PRIVATE);
int value = prefs.getInt("ComandToDo", -1);    

And here comes the problem because "value" is not the value written in the thread.

Ton
  • 9,235
  • 15
  • 59
  • 103
  • 3
    Are you doing these reads and writes in different threads? Post the code where you read and write. – Khantahr Jan 02 '13 at 19:40
  • yes, diffetent threads, activities, broadcast receivers, ... many places. Imagine: a thread writes a value in SharedPreferences. Then it creates an alarm. The broadcastreceiver reads that value some minutes later. But it is not updated! – Ton Jan 02 '13 at 19:45
  • Need to see your code where you read and write the values too. – Khantahr Jan 02 '13 at 19:49
  • 2
    `SharedPreferences` is thread-safe, but not process-safe. If you have set up your app to use multiple processes, you can encounter this problem. In that case, the simplest solution is to get rid of the multiple processes. – CommonsWare Jan 02 '13 at 19:55
  • 1
    Will I get the same problem if I change SharedPreferences for a table in a database? – Ton Jan 02 '13 at 20:51

4 Answers4

6

Here is what I encountered and what I did to fix it.

I was triggering an alarm from an Activity and in Broadcast-Receiver of that alarm I was updating Shared-Preferences which were read every time the app was launched.

After the alarm was triggered, whenever the app was launched it would get old values which were set from that activity only. No changes from Broadcast-Receiver were reflected.

The trick here is to set Shared-Preferences as MODE_MULTI_PROCESS

Generally we use MODE_PRIVATE, but do as follows:

SharedPreferences prefs = this.getSharedPreferences("Preferences", MODE_MULTI_PROCESS);

Note: After changing mode in code, it is advised to clear data of app to avoid issues while debugging.

EDIT: MODE_MULTI_PROCESS need min API 11

Before API 11 the workaround which I can think of is creating a database with 2 columns KEY & VALUE. This can be accessed from other modules.

Kapil Jituri
  • 1,241
  • 14
  • 25
3
  1. SharedPreferences are documented not to work across processes, http://developer.android.com/reference/android/content/SharedPreferences.html, "Note: currently this class does not support use across multiple processes. This will be added later."

  2. This answer recommends encapsulation of data into a content provider, and the discussion also considers some other options, including shared SQLite: https://stackoverflow.com/a/5265556/1665128

  3. You also have plain old files in the file system. We used them in several projects, with locking, without any issues. May be an option for you as well.

Community
  • 1
  • 1
full.stack.ex
  • 1,747
  • 2
  • 11
  • 13
  • Thanks! One more question. It is not really two processes steping on each other. It is just one thread that records something to do and triggers and alarm (maybe for hours later). That simple! So, is there a way to force writing to disk and leave caching? Thanks – Ton Jan 03 '13 at 06:21
  • @Ton: it indeed looks strange for a mobile system not to commit stuff as soon as possible. Just in case, by any chance, could it be a logical error, despite all the simplicity? Something crazy, like a third piece of code that happens to overwrite the commit, control somehow not reaching the save point (an exception in "some code later"), etc. How about peppering it with Log.d? (Also, it would be interesting to look inside the Android code; I don't have any handy right now, but, AFAIK, behind the scenes those preferences are persisted in files.) – full.stack.ex Jan 03 '13 at 10:19
  • 4
    As of this writing [SharedPreferences](http://developer.android.com/reference/android/content/SharedPreferences.html) still says "Note: currently this class does not support use across multiple processes. This will be added later." BUT, API 11 added [MODE_MULTI_PROCESS](http://developer.android.com/reference/android/content/Context.html#MODE_MULTI_PROCESS) which, "when set, the file on disk will be checked for modification even if the shared preferences instance is already loaded in this process..." So, you should be able to use `getSharedPreferences(PREFS_NAME, Context.MODE_MULTI_PROCESS)` – Anonsage Jun 04 '13 at 00:07
0

You are writing in "MyPrefs" preference file and then trying to read from "PerfilDeSonidoPreferencias" file. Please read/write from same preference file.

Munish Katoch
  • 517
  • 3
  • 6
  • Sorry, that was just a mistake when copying-pasting. Both are "MyPrefs" or whatever. – Ton Jan 02 '13 at 23:12
0

Just use IntentService instead BroadcastReceiver. It`s work for me.

ServiceClass:

public class ServiceClass extends IntentService {

    public NotificationPopupService(String name) {
        super(name);
    }

    public NotificationPopupService(){
        super("YOU_DEFAULT_NAME");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());

        do magic...
    }
}

In you AndroidManifest.xml file in application tag:

<service android:name="ServiceClass"/>

Create alarm:

intent = new Intent(context, ServiceClass.class);
pendingIntent = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// magic after 2 seconds after the creation of alarm
alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 2000, pendingIntent);
Sergei Bubenshchikov
  • 5,275
  • 3
  • 33
  • 60