Should Accessing Sharedpreferences Be Done Off the UI Thread

Should accessing SharedPreferences be done off the UI Thread?

I'm glad you're already playing with it!

Some things to note: (in lazy bullet form)

  • if this is the worst of your problems, your app's probably in a good spot. :) Writes are generally slower than reads, though, so be sure you're using SharedPreferenced$Editor.apply() instead of commit(). apply() is new in GB and async (but always safe, careful of lifecycle transitions). You can use reflection to conditionally call apply() on GB+ and commit() on Froyo or below. I'll be doing a blogpost with sample code of how to do this.

Regarding loading, though...

  • once loaded, SharedPreferences are singletons and cached process-wide. so you want to get it loaded as early as possible so you have it in memory before you need it. (assuming it's small, as it should be if you're using SharedPreferences, a simple XML file...) You don't want to fault it in the future time some user clicks a button.

  • but whenever you call context.getSharedPreferences(...), the backing XML file is stat'd to see if it's changed, so you'll want to avoid those stats during UI events anyway. A stat should normally be fast (and often cached), but yaffs doesn't have much in the way of concurrency (and a lot of Android devices run on yaffs... Droid, Nexus One, etc.) so if you avoid disk, you avoid getting stuck behind other in-flight or pending disk operations.

  • so you'll probably want to load the SharedPreferences during your onCreate() and re-use the same instance, avoiding the stat.

  • but if you don't need your preferences anyway during onCreate(), that loading time is stalling your app's start-up unnecessarily, so it's generally better to have something like a FutureTask<SharedPreferences> subclass that kicks off a new thread to .set() the FutureTask subclasses's value. Then just lookup your FutureTask<SharedPreferences>'s member whenever you need it and .get() it. I plan to make this free behind the scenes in Honeycomb, transparently. I'll try to release some sample code which
    shows best practices in this area.

Check the Android Developers blog for upcoming posts on StrictMode-related subjects in the coming week(s).

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.

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.

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.

Trying to establish best practices for SharedPreferences

    public boolean getPrefs()
{

prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());

PREF1 = prefs.getBoolean("pref1_key",pref1_default);
PREF2 = prefs.getString("pref2_key","pref2_default");

}

use this method to get all preferences you need. Call this in the onResume() or onCreate() method. You needn't worry about UI or anything

Android SharedPreferences Best Practices

If you have a large application that is relying on SharedPreferences you could have key duplication, especially in the case of using some third party library that relies on SharedPreferences as well.

Libraries should not use that particular SharedPreferences. The default SharedPreferences should only be used by the application.

This way if you have a class that heavily relies on SharedPreferences you can create a preference file that is used only by your class.

You are certainly welcome to do this. I wouldn't, at the application level, as the primary reason for SharedPreferences is to have them be shared among the components in the application. A development team should have no problem managing this namespace, just as they should have no problem managing names of classes, packages, resources, or other project-level stuff. Moreover, the default SharedPreferences are what your PreferenceActivity will use.

However, going back to your libraries point, reusable libraries should use a separate SharedPreferences for their library only. I would not base it on a class name, because then you are one refactoring away from breaking your app. Instead, pick a name that is unique (e.g., based on the library name, such as "com.commonsware.cwac.wakeful.WakefulIntentService") but stable.

it seems that accesses SharedPreferences should be done off the UI thread which makes sense.

Ideally, yes. I recently released a SharedPreferencesLoader that helps with this.

Are there any other best practices Android developers should be aware of when using SharedPreferences in their applications?

Don't over-rely upon them. They are stored in XML files and are not transactional. A database should be your primary data store, particularly for data you really don't want to lose.

Can I access shared preferences (save/edit) from Main Thread?

It's pretty light. If there's any doubt, you can use SharedPreferences.Editor#apply() which writes to disk asynchronously instead of commit() which writes synchronously.



Related Topics



Leave a reply



Submit