There are 2 intended purposes for shared preferences in Android. First of these is a persistence mechanism to quickly remember user preferences for android apps. In that context Android has a declarative framework for generating the UI and also the persistence of the values chosen in the UI.

The second use is an extension of the first minus the UI. In this context preferences are merely key/value pairs that are stored by the application at any point of its life with no constraints of the UI.

Inventive programmers have finagled the second usage pattern to store arbitrary java objects as JSON strings and persist them using the preferences key/value pair APIs.

See the ProAndroid series to understand the first two usage patterns well. The book also shows how home screen widget state can be maintained in preferences.

In the upcoming Expert Android book I am going to write about the unintended, but quite useful, json usage pattern using the shared preferences.

This article is a quick rehash of the links and the code snippets that I am collecting which in due time will make their way to the Expert android book.

satya - Wed Dec 26 2012 11:28:57 GMT-0500 (Eastern Standard Time)

Here is where I have used shared preference to store widget state

Here is where I have used shared preference to store widget state

satya - Wed Dec 26 2012 11:30:06 GMT-0500 (Eastern Standard Time)

Here is how shared preferences show up in data storage options for Android

Here is how shared preferences show up in data storage options for Android

satya - Wed Dec 26 2012 11:30:48 GMT-0500 (Eastern Standard Time)

A quick tour of shared preferences from Android

A quick tour of shared preferences from Android

satya - Wed Dec 26 2012 11:31:28 GMT-0500 (Eastern Standard Time)

API doc for SharedPreferences

API doc for SharedPreferences

satya - Wed Dec 26 2012 11:32:17 GMT-0500 (Eastern Standard Time)

Saving shared preferences through the Editior API

Saving shared preferences through the Editior API

satya - Wed Dec 26 2012 11:35:13 GMT-0500 (Eastern Standard Time)

How are shared preferences stored?

All the key value pairs are stored as an XML file.

satya - Wed Dec 26 2012 11:37:07 GMT-0500 (Eastern Standard Time)

In android where are the shared preferences xml file kept?


/data/data/YOUR_PACKAGE_NAME/shared_prefs/YOUR_PREFS_NAME.xml
or the default is at
/data/data/YOUR_PACKAGE_NAME/shared_prefs/YOUR_PACKAGE_NAME_preferences.xml

satya - Wed Dec 26 2012 11:43:53 GMT-0500 (Eastern Standard Time)

Example of usage


int mode;
//One of 
MODE_PRIVATE (value of 0 and default)
MODE_WORLD_READABLE
MODE_WORLD_WRITEABLE
MODE_MULTI_PROCESS

SharedPreferences sp = activity.getSharedPreferences("myprefs.xml");

You may not know if the file exists.

satya - Wed Dec 26 2012 11:45:38 GMT-0500 (Eastern Standard Time)

Because it is created the first time when you do this


SharedPreferences.Editor spe = esp.edit();
spe.put...();
spe.commit();

satya - Wed Dec 26 2012 11:46:31 GMT-0500 (Eastern Standard Time)

You can do this to get to the single default preferences activity


SharedPreferences default_sp = activity.getPreferences();
//No name is required

satya - Wed Dec 26 2012 11:52:04 GMT-0500 (Eastern Standard Time)

Why commit??

If this preferences file is shared among processes, a commit will take it to the persistent state and make it visible to the other clients. A single file will return a shared preferences object that is one or at least refer to the same file when in a different process.

satya - Wed Dec 26 2012 15:43:52 GMT-0500 (Eastern Standard Time)

SharedPreferences is an interface and it uses the following files


SharedPreferencesImpl.java
XmlUtils.java

satya - Thu Dec 27 2012 10:38:29 GMT-0500 (Eastern Standard Time)

getPreferences API link

getPreferences API link

The activity docs suggest to use this method to save persistence state of this activity as key value pairs if needed.

This method calls the underlying context.getSharedPreferences(name, mode) with name as the classname of the activity.

satya - Thu Dec 27 2012 10:46:41 GMT-0500 (Eastern Standard Time)

Here is how you can store a context at the application level


public class MyApplication extends Application
{
   public final static String tag="MyApplication";
   public static Context s_applicationContext = null;

   @Override
   public void onConfigurationChanged(Configuration newConfig) {
      super.onConfigurationChanged(newConfig);
      Log.d(tag,"configuration changed");
   }

   @Override
   public void onCreate() {
      super.onCreate();
      s_applicationContext = getApplicationContext();
      Log.d(tag,"oncreate");
   }

   @Override
   public void onLowMemory() {
      super.onLowMemory();
      Log.d(tag,"onLowMemory");
   }

   @Override
   public void onTerminate() {
      super.onTerminate();
      Log.d(tag,"onTerminate");
   }

}

satya - Thu Dec 27 2012 10:51:56 GMT-0500 (Eastern Standard Time)

How to test escape characters in shared preferences


public void testEscapeCharactersInPreferences()
   {
      String testString = "<node1>blabhhh</ndoe1>";
      SharedPreferences sp = getSharedPreferences();
      SharedPreferences.Editor spe = sp.edit();
      spe.putString("test", testString);
      spe.commit();
      String savedString = sp.getString("test", null);
      if (savedString == null)
      {
         mReportTo.reportBack(tag,"no saved string");
         return;
      }
      //savedstring exists
      mReportTo.reportBack(tag,savedString);
      if (testString.equals(savedString))
      {
         mReportTo.reportBack(tag,"Saved the string properly. Match");
         return;
      }
      //they dont match
      mReportTo.reportBack(tag,"They don't match");
      return;
   }
   private SharedPreferences getSharedPreferences()
   {
      SharedPreferences sp 
      = MyApplication.s_applicationContext.getSharedPreferences("myprefs", Context.MODE_PRIVATE);
      return sp;
   }

satya - Thu Dec 27 2012 10:57:50 GMT-0500 (Eastern Standard Time)

Good news. It does seem to escape the chars properly

Good news. It does seem to escape the chars properly

satya - Thu Dec 27 2012 11:06:42 GMT-0500 (Eastern Standard Time)

Here is how this prefs file look like


<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="test">&lt;node1&gt;blabhhh&lt;/ndoe1&gt;</string>
<string name="json">{&quot;childList&quot;:[{&quot;name&quot;:&quot;Adam&quot;,&quot;likesVeggies&quot;:false,
&quot;age&quot;:30},{&quot;name&quot;:&quot;Eve&quot;,&quot;likesVeggies&quot;:false,
&quot;age&quot;:28}],&quot;stringArray&quot;:[&quot;first&quot;,&quot;second&quot;],
&quot;strinValue&quot;:&quot;st\u003cri\u003e\&quot;ng\&quot;Value\u003cnode1\u003etest\u003c/node2\u003e
&quot;,&quot;intValue&quot;:5}</string>
</map>

satya - Thu Dec 27 2012 11:07:46 GMT-0500 (Eastern Standard Time)

The string that we are particularly looking for is


<string name="test">&lt;node1&gt;blabhhh&lt;/ndoe1&gt;</string>

satya - Thu Dec 27 2012 11:09:11 GMT-0500 (Eastern Standard Time)

The second one is JSON as produced by gson

gson will have produced a lot of "" marks natively. These are further converted by preferences API. In the end though the fidelity is maintained across all these conversions!!!

satya - Thu Dec 27 2012 11:10:34 GMT-0500 (Eastern Standard Time)

Read my note on GSON and JSON here

Read my note on GSON and JSON here

satya - Thu Dec 27 2012 11:22:33 GMT-0500 (Eastern Standard Time)

Given JSON and a set of objects you can do this!!


public void storeJSON()
{
    MainObject mo = MainObject.createTestMainObject();
    //
    Gson gson = new Gson();
    String jsonString = gson.toJson(mo);
    this.mReportTo.reportBack(tag, jsonString);
    MainObject mo1 = gson.fromJson(jsonString, MainObject.class);
    this.mReportTo.reportBack(tag, jsonString);
    
    SharedPreferences sp = getSharedPreferences();
    SharedPreferences.Editor spe = sp.edit();
    spe.putString("json", jsonString);
    spe.commit();
}

public void retrieveJSON()
{
    SharedPreferences sp = getSharedPreferences();
    String jsonString = sp.getString("json", null);
    if (jsonString == null)
    {
        mReportTo.reportBack(tag,"Not able to read the preference");
        return;
    }
    Gson gson = new Gson();
    MainObject mo = gson.fromJson(jsonString, MainObject.class);
    mReportTo.reportBack(tag,"Object successfully retrieved");
    String compareResult = MainObject.checkTestMainObject(mo);
    if (compareResult != null)
    {
        //there is an error
        mReportTo.reportBack(tag,compareResult);
        return;
    }
    //compareReesult is null
    mReportTo.reportBack(tag,"Retrieved object matches");
    return;
}

satya - Thu Dec 27 2012 11:23:29 GMT-0500 (Eastern Standard Time)

Both GSON and SharedPreferences are smart enough to escape special delimiters quite nicely I must say!!

Both GSON and SharedPreferences are smart enough to escape special delimiters quite nicely I must say!!