Use SharedPreferences on multi-process mode
Asked Answered
M

8

24

I've defined an instance of SharedPreferences that used on multi-process mode.

public class Prefs {

    private static SharedPreferences prefs;
    private static SharedPreferences.Editor editor;

    private static void init(Context context) {

        prefs = context.getSharedPreferences("alaki",
                Context.MODE_MULTI_PROCESS);
        editor = prefs.edit();
    }

// static methods to set and get preferences
}

Now I'm using this class on a service with separate process and also in my main application process in static way.
Everything is going well, but sometimes all stored data on SharedPreferences instance removed!
How can I solve this problem?

Edit: Finally I've solved my problem using by IPC.

Macule answered 7/1, 2015 at 20:1 Comment(4)
Are you calling editor.commit after making changes?Counterblow
Hmm. What do you mean when you say all the data is removed? Is one process modifying the prefs and the other not seeing the changes?Counterblow
@jmm No, Both of processes can see and modify but my data is not safe! for example, after half hour all data removed!Macule
See this: #22130217Dependency
G
39

There is currently no way of safely accessing SharedPreferences on multiple processes, as described in its documentation.

Note: This class does not support use across multiple processes.

After testing a lot with MODE_MULTI_PROCESS, I've three trials to share:

1- Initialize the SharedPreferences once in each process and use it multiple times.

The problem: The values are not reflected in each process as expected. So each process has its own value of the SharedPreferences.

2- Initialize the SharedPreferences in each put or get.

This actually works and the value now is interchangeable between processes.

The problem: sometimes after aggressively accessing the sharedpref, the shared preferences file got deleted with all its content, as described in this issue, and I get this warning in the log:

W/FileUtils﹕ Failed to chmod(/data/data/com.hegazy.multiprocesssharedpref/shared_prefs/myprefs.xml): android.system.ErrnoException: chmod failed: ENOENT (No such file or directory)

You can find why this happens in the issue.

3- Use synchronization to lock the methods that put and get values in the SharedPreferences.

This is completely wrong; synchronization doesn't work across processes. The SharedPreferences is actually using synchronization in its implementation, but that only ensures thread safety, not process safety. This is described very well here.

Gel answered 16/1, 2015 at 16:2 Comment(8)
Thanks for this well-researched answer! I found it a bit confusing to read, so I edited it to tidy up the English so it's easier to follow. Hopefully I've preserved your original meaning.Elbertina
@AhmedHegazy Your answer is quite risky, I think you should consider send the value or object to the service or wise every time that stored value or object change in the preference you send a message to the service with the updated value. That's what I do, I just read or store the values in the preference from one of the two process.Chevaldefrise
@AlexSanchez Sorry, that what the answer is about. It says that you can't access the shared pref from multiple processes.Gel
The sentence "This will be added later" has now been removed.Meredeth
@Meredeth oh! I was not waiting though :DGel
initializing the sharedorefrences everytime does the job, but does this lead to any performance problems ?Hoppe
@TamimProduction yeah as written above it might produce an issue after heavily accessing the sharedprefGel
@AhmedHegazy in my case i'm using it to store currently running and pending tasks and display a disabled button so the user doesn't duplicate tasks, i use it once on task added to queue , and once on task finish, do you think that's a dirty approach ?Hoppe
F
8

SharedPreferences itself is not process-safe. That's probably why SharedPreferences documentation says

Note: currently this class does not support use across multiple processes. This will be added later.

Federalese answered 16/1, 2015 at 10:8 Comment(0)
E
3

I've worked around this by combining:

  • Providing each process mutually-exclusive access to the SharedPreferences file (such as by using a socket-based locking mechanism)
  • Re-initialising the SharedPreferences with the MODE_MULTI_PROCESS flag every time you want to use it to bypass in-memory caching

This seems to work OK, but it hasn't been thoroughly tested in the real world, so I don't know if it's perfectly reliable.

You can see a working example I wrote here.

Warning: Looks like MODE_MULTI_PROCESS has been deprecated in Android M. It might stop working in the future.

Elbertina answered 9/7, 2015 at 13:21 Comment(1)
Your efforts to solve the problem with creative methods is admirableMacule
S
2

Using the commit() method store the changes in persistent storage, hence it is slow and would make conflict across multiple call from other processes.

However there is an alternative to this method, you should call the apply() method, this method stores the changes in memory and then in disk storage asynchronously, so it is more reliable.

Soche answered 15/1, 2015 at 21:30 Comment(1)
Why do you think asynchronously writing the preferences file will reduce clashes with other processes? I just double-checked the SharedPreferencesImpl source code, and they both use the same code (enqueueDiskWrite) to write the preferences file.Elbertina
C
2

Use a Content Provider which uses SharedPreferences. Example see here: https://github.com/hamsterksu/MultiprocessPreferences

Cityscape answered 22/5, 2017 at 11:23 Comment(2)
I've been using this technique in production for quite a while now. It generally works but also has various occasional issues that appear to be bugs in Android related to content providers.Elbertina
It will work but will cause some ANRs.Branny
G
1

recalls that the use of context objects as static field, you have the risk of leakage of context because not declare the object in the application class

public class CustomApplication extends Application{
     private Prefs prefs;

     public void onCreate(){
          prefs = new Prefs(this);
     }

     public Prefs getPrefs(){
        return prefs;
     }
}

From any context you can get the prefs

   ((MyApplication)context.getApplicationContext()).getPrefs();
Gronseth answered 15/1, 2015 at 21:56 Comment(0)
Q
0
 public static int getValore(Context ctx, String contenitore,  String chiave, int valore){
    try {
        SharedPreferences sh = ctx.getApplicationContext()
                .getSharedPreferences(contenitore, Context.MODE_MULTI_PROCESS);
        //SharedPreferences.Editor editor = sh.edit();
        return sh.getInt(chiave, valore);
    }catch (Exception ex){
        return valore;
    }
}
Quinquagesima answered 23/9, 2022 at 15:55 Comment(0)
H
-2

If two processes write data to SharedPreferences, then it might possible all SharedPreferences are reset to default values.

Also you can try to call clear() on the editor before storing val

SharedPreferences.Editor sp = settings.edit();
sp.clear();
sp.putString("Name", "YourName");
sp.commit();
Hydroquinone answered 9/1, 2015 at 9:46 Comment(1)
clear() return all data to default values. but @Mousa wants to not clear data suddenly.Shirleeshirleen

© 2022 - 2024 — McMap. All rights reserved.