12

I am having an odd issue in which the SharedPreferences are not being updated upon returning to an app. Here's the scenario:

I have two projects that use the same shared preferences. Project1 and Project2. They are separate but related apps. They are signed with the same key and use sharedUserId to share information.

Project1 opens Project2.

Project2 retrieves the SharedPreferences file and writes to it via this method:

Context prefsContext = c.createPackageContext(packageNameOfProject1, Context.CONTEXT_IGNORE_SECURITY);
SharedPreferences prefs = prefsContext.getSharedPreferences(fileName, Context.MODE_PRIVATE);
SharedPreferences.editor editor = prefs.edit();
editor.putBool("bool1", value1);
editor.putBool("bool2", value2);
...
editor.putBool("boolN", valueN);
editor.apply();

Once that is done, I return to Project1 by calling finish().

Project1 then reads the data like so:

SharedPreferences prefs = getSharedPreferences(getPreferencesFileName(), Context.MODE_PRIVATE);
Boolean value1 = prefs.getBoolean(fileName, false);
Boolean value2 = prefs.getBoolean(fileName, false);
...
Boolean valueN = prefs.getBoolean(fileName, false);
Map<String, ?> mappings = prefs.getAll();
Set<String> keys = mappings.keySet();
for(String key : keys) {
  log.d(TAG, "_____");
  log.d(TAG, "Key = " + key);
  log.d(TAG, "Value = " + mappings.get(key));
}

The problem is the values are not updated in Project1. I can tell based off the logs at the end that the file isn't even generating mappings. However, I can verify that the xml is being updated. If I force stop the app then restart it, all the mappings are there in Project1. All the values are correct. However, I need them updated when the user leaves Project2. I feel like there's something I'm missing here but can not spot it.

The only things I have been able to find on the subject is:

SharedPreferences.Editor not being updated after initial commit

SharedPreferences value is not updated

These don't help as I'm already doing that.

I have WRITE_EXTERNAL_STORAGE set in both manifests. The fileName is the same (else I wouldn't be able to read the file when I reenter the app).

EDIT:

I should note that I did try to do editor.commit() instead of editor.apply() as I thought I was facing a race condition. The problem still persisted. I'm thinking that for some reason, the old reference to the SharedPreference in Project1 is being used instead of a new one even though I'm lazy-loading it each time.

EDIT2:

Ok, to further test to see what id going on. I decided to try the opposite direction.

In Project1 I do:

Float testFloat (float) Math.random();
Log.d("TEST_FLOAT", "Project1: TEST_FLOAT = " + testFloat);
prefs.edit().putFloat("TEST_FLOAT", testFloat).commit();

In Project2 I do:

Log.d("TEST_FLOAT", "Project2: TEST_FLOAT = " + prefs.getFloat("TEST_FLOAT", 0.0f));

I then go back and forth between the two like so: Project1->Project2->Project1->Project2->Project1->Project2 and here is the logcat result:

Project1: TEST_FLOAT = 0.30341884
Project2: TEST_FLOAT = 0.30341884
Project1: TEST_FLOAT = 0.89398974
Project2: TEST_FLOAT = 0.30341884
Project1: TEST_FLOAT = 0.81929415
Project2: TEST_FLOAT = 0.30341884

In other words, it's reading and writing to the same file. However, it's keeping the mapping that it had when it was first opened it in the project. Even though I close the project, the mapping remains until the application is force stopped.

Cœur
  • 37,241
  • 25
  • 195
  • 267
DeeV
  • 35,865
  • 9
  • 108
  • 95
  • Are you doing this SharedPreferences prefs = getSharedPreferences(getPreferencesFileName(), Context.MODE_PRIVATE); on OnCreate? – VendettaDroid Sep 27 '12 at 22:34
  • No. Much later than that. In Project1, it would be in the onCreateView() of the fragment I'm using and later in onResume(). In Project2, I am lazy-loading the prefs and writing data to the file as it becomes available. – DeeV Sep 27 '12 at 22:42

3 Answers3

25

EDIT: I'm still getting upvotes on this answer even though it recommends a method that has since been deprecated. If you need consistent data through multi-process, then you need to use something other than SharedPreferences like a ContentProvider backed by a file system or database.

https://developer.android.com/reference/android/content/Context#MODE_MULTI_PROCESS


Final answer:

Replace

getSharedPreferences(fileName, Context.MODE_PRIVATE);

with

getSharedPreferences(fileName, Context.MODE_MULTI_PROCESS);

As per document:

Context.MODE_MULTI_PROCESS

SharedPreferences loading flag: when set, the file on disk will be checked for modification even if the shared preferences instance is already loaded in this process. This behavior is sometimes desired in cases where the application has multiple processes, all writing to the same SharedPreferences file. Generally there are better forms of communication between processes, though.

This was the legacy (but undocumented) behavior in and before Gingerbread (Android 2.3) and this flag is implied when targeting such releases. For applications targeting SDK versions greater than Android 2.3(Gingerbread), this flag must be explicitly set if desired.

I knew there was a simple oversight in this.

DeeV
  • 35,865
  • 9
  • 108
  • 95
  • Hi, Thanks for the answer. i am facing same issue. Context.MODE_MULTI_PROCESS works fine in 2.3+ versions but i am not getting updated values in android 2.2 . Tried with MODE_PRIVATE and 0 value.. only getting default values. can u help me? – djk Apr 24 '13 at 10:46
  • 2
    To anybody stumbling across this today. This was the answer in 2012, but it's not good today. `MODE_MULTI_PROCESS` has long been deprecated. Multi process access must be done through another means. https://developer.android.com/reference/android/content/Context.html#MODE_MULTI_PROCESS – DeeV Apr 18 '18 at 21:13
  • such a helpful answer, thanks! – Jonathan Applebaum Feb 10 '22 at 18:05
1

Try to call editor.commit(); instead of editor.apply();. Normally they should do the same, but I noticed there some weird behaviour sometimes.

Leandros
  • 16,805
  • 9
  • 69
  • 108
1

From the SharedPreferences documentation the method "apply()" writes asynchronously (delayed) to the file and the method "commit()" writes the information synchronously (immediatly) to the file.

Also from the documentation, they say that you don't need to wary about the activity life cycle while using any of the above methods, as they ensure the "apply()" writes are completed before status changes, if they are running in the same system process.

However, as you are using two different projects, they run in two different processes and you can't be sure that "apply()" on project 2 will be concluded before the "onResume()" starts on project 1.

I suggest that you try "commit()" instead of "apply()" to force synchronous write. If this don't solve the issue, you can add a delay of a couple of seconds before reading the preferences in project 1, just to check if the issue is related to this delayed write.

--EDITED--

To debug the issue let's do the following:

1-In Eclipse Select/Add the view "File Explorer" and navigate to the directory:

/data/data/[your package name]/shared_prefs

your package name should be something like "com.myproject.shared"

2-Select the file with your saved preferences and press the button "download to PC".

3-Check if file contents match your expectations.

good luck.

Luis
  • 11,978
  • 3
  • 27
  • 35
  • I did try that thinking the same thing. However, it didn't make any difference. It's almost as if the SharedPreferences reference I'm getting in Project1 is the same one I get when I first open it. Rather than creating a new one in memory it just gives me the old, out-dated one. – DeeV Sep 28 '12 at 13:17
  • Can you try the debug I suggest in the edited part of the answer and let me know if it helped? – Luis Sep 28 '12 at 13:34
  • Have in mind that if you are using two diffrent pakage names for your projects, you will end up with two different files with same name but in different locations, one per package. – Luis Sep 28 '12 at 13:42
  • Yes. I am aware of that. I can tell that they are reading/writing from the same location because the file does get updated. However, I can verify that the file opened in Project1 does not update until the app is restarted. On the device, it is written correctly, but the app doesn't read it right. Once I restart the app, it opens the file and all the data is read properly. The file is update at this time. What I'm guessing is the file that's opened when you get SharedPreferences is a temp file that doesn't get closed until the memory is removed. – DeeV Sep 28 '12 at 14:31
  • Hey. Thanks for the help. In the end I was overlooking the `Context.MODE_MULTI_PROCESS` flag which checks if the SharedPreferences had been modified since last access. I upvoted since what you said wasn't wrong. It just wasn't the problem. – DeeV Sep 28 '12 at 17:14