Sharedpreferences and Thread Safety

SharedPreferences and Thread Safety

Processes and Threads are different. The SharedPreferences implementation in Android is thread-safe but not process-safe. Normally your app will run all in the same process, but it's possible for you to configure it in the AndroidManifest.xml so, say, the service runs in a separate process than, say, the activity.

To verify the thready safety, see the ContextImpl.java's SharedPreferenceImpl from AOSP. Note there's a synchronized wherever you'd expect there to be one.

private static final class SharedPreferencesImpl implements SharedPreferences {
...
public String getString(String key, String defValue) {
synchronized (this) {
String v = (String)mMap.get(key);
return v != null ? v : defValue;
}
}
...
public final class EditorImpl implements Editor {
public Editor putString(String key, String value) {
synchronized (this) {
mModified.put(key, value);
return this;
}
}
...
}
}

However for your case of the unique id it seems you'd still want a synchronized as you don't want it to change between the get and the put.

SharedPreferences thread safety

As discussed in the post you linked. Unless you specifically tells Android in you manifest to run your service in another process it should be safe to write and read at the same time.

So it would probably be fine your case.

Android Thread Safe SharedPreferences

You might not like this answer.

You are not using the right system here. SharedPreferences is for storing simple preferences. Just because you can do this does not mean you should. You're basically trying to make SharedPreferences into something its not. You can add all this fancy locking but it won't stop someone from coming in underneath this later and accidentally blowing it up.

If you find yourself needing these feature in earnest, you should look at just using sqlite directly. There is little doubt you could add synchronization to SharedPreferences (and I am sure it is safe to some degree as it is already designed with a transaction/commit model) but it seems to me like reinventing the wheel.

Accessing SharedPreferences On Separate Thread

Is editing shared preferences in a separate thread redundant if you use apply?

Yes, but bear in mind that you may not only be editing SharedPreferences. You may be reading them as well.

Given the nature of your code, my guess is that you're calling it as one of the first things in your LAUNCHER activity. If so, nothing else will have retrieved those SharedPreferences yet, and so you will get disk-read violations from StrictMode for the reading, not so much the editing.

Since you already have the background thread, I'd switch to commit() rather than use apply() and waste another thread.

Can and Should we commit SharedPreferences on NON UI thread, if yes how ?

so is there a way to save and retrieve data on NON-UI thread.

SharedPreferences are cached. The first time you try accessing a given SharedPreferences (e.g., getSharedPreferences()), there will be disk I/O. You are welcome to do this work on a background thread, sometime in advance of when you need the preferences.

You can call apply(), rather than commit(), to persist changes to the SharedPreferences on a framework-supplied background thread.

Cant access value of SharedPreference in different thread

The thread which was accessing the shared preference turned out to be in a service which was running in a different process,

so changing the shared preference mode to MODE_MULTI_PROCESS instead of MODE_PRIVATE worked perfectly,

Now I can access the shared preference normally

Thanks for all who tried!

Is it safe to keep a static reference to a SharedPreferences and its Editor?

To answer the question authoritatively, it is safe to store the SharedPreferences instance as a static reference. According to the javadocs it is a singleton, so its source from getSharedPreferences is already a static reference.

It is not safe to store the SharedPreferences.Editor because it is possible two threads may be manipulating the same editor object at the same time. Granted, the damage this would cause is relatively minor if you happen to have already been doing it. Instead, get an instance of an editor in each editing method.

I highly recommend using a static reference to your Application object instead of passing in Context objects for every get. All instances of your Application class are singletons per process anyways, and passing around Context objects is usually bad practice because it tends to lead to memory leaks via reference holding, and is unnecessarily verbose.

Finally, to answer the unasked question if you should lazily-load or greedily-initialize the reference to your static SharedPreferences, you should lazily load in a static getter method. It may work to greedily-initialize a reference with final static SharedPreferences sReference = YourApplication.getInstance().getSharedPreferences() depending on the chain of class imports, but it would be too easy for the class loader to initialize the reference before the Application has already called onCreate (where you would initialize the YourApplication reference), causing a null-pointer exception. In summary:

class YourApplication {
private static YourApplication sInstance;

public void onCreate() {
super.onCreate();
sInstance = this;
}
public static YourApplication get() {
return sInstance;
}
}

class YourPreferencesClass {
private static YourPreferencesClass sInstance;
private final SharedPreferences mPrefs;

public static YourPreferencesClass get() {
if (sInstance == null)
sInstance = new YourPreferencesClass();
return sInstance;
}

private final YourPreferencesClass() {
mPrefs = YourApplication.get().getSharedPreferences("Prefs", 0);
}

public void setValue(int value) {
mPrefs.edit().putInt("value", value).apply();
}

public int getValue() {
return mPrefs.getInt("value", 0);
}
}

You will then use your statically available preferences class as such:

YourPreferencesClass.get().setValue(1);

A final word about the thread-safety and memory observability. Some astute observers may notice that YourPreferencesClass.get() isn't synchronized, and hence dangerous because two threads may initialize two different objects. However, you can safely avoid synchronization. As I mentioned earlier, getSharedPreferences already returns a single static reference, so even in the extremely rare case of sInstance being set twice, the same underlying reference to SharedPreferences is used. Regarding the static instance of YourApplication.sInstance, it is also safe without synchronization or the volatile keyword. There are no user threads in your application running before YourApplication.onCreate, and therefore the happens-before relationship defined for newly created threads ensures that the static reference will be visible to all future threads that may access said reference.

Does supplying Context.MODE_MULTI_PROCESS to getSharedPreferences() make the shared prefs thread safe?

As per this answer, shared preferences are thread-safe. What the Android docs said is that they aren't multi-process safe.

https://stackoverflow.com/a/4695567/855680



Related Topics



Leave a reply



Submit