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.

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

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

A quick tour of shared preferences from Android

API doc for SharedPreferences

Saving shared preferences through the Editior API

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


/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

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.


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

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

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.


SharedPreferencesImpl.java
XmlUtils.java

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.


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");
   }

}

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;
   }

Good news. It does seem to escape the chars properly


<?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>

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

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!!!

Read my note on GSON and JSON here


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;
}

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