Saturday, March 10, 2012

Android InDepth : SharedPreference



1. Introduction

SharedPreference is used to store settings/preferences. It stores data as key/value pairs. It share data between activities but not across the processes.

Shared Preference is stored in /data/data/[package name]/shared_prefs/[SP Name].xml.

In Device data/data folder is not accessible but we can access data/data folder in Emulator. Using DDMS allow access to every folders including data/data folder. It is possible to pull  the Shared Preference  xml file to PC. If we stuck in any problem related to Shared Preference then we can transfer xml file to PC for observation.


2. Implementation


Let's Create simple example of SharedPreference. SharedPreference is Interface. We do not need to create object of SharedPreference. It created with the Activity. I will explain how SharedPreference Object will crate in this tutorial. Activity has method named "getSharedPreference()" we can retrieve object like following.

2.1 Retrieving object of SharedPreference
SharedPreference pref = getSharedPreference("MyPRef",MODE_PRIVATE);
Method has two parameter Name and Mode, Name is used as XML file name. 

  • MODE_PRIVATE is the operating mode for the preferences. It is the default mode and means the created file will be accessed by only the calling application. 
  • MODE_WORLD_READABLE other application can read the created file but can not modify it. 
  • MODE_WORLD_WRITEABLE other applications also have write permissions for the created file. 
First mode is default we can implement in this example. but what about other two options.  I found following post in "StackOverFlow.com"


"As I checked in APIs description for getSharedPreferences(String, int),Second attribute is defining accessibility mode and can take 0 or MODE_PRIVATE for the default operation, MODE_WORLD_READABLE and MODE_WORLD_WRITEABLE to control permissions.

But there is this small note in API description:

Note: currently this class (android.content.SharedPreferences) does not support use across multiple processes. This will be added later.

Moreover in Mark L. Murphy book "beginning Android 2" he mentioned:

(Eventually, preferences might be shareable across applications, but that is not supported as of the time of this writing)

Im so confused! does this mean that MODE_WORLD_READABLE and MODE_WORLD_WRITEABLE of getSharedPrefrences is there but NOT SUPPORTED YET in latest API level???"
Source : Here

2.2 Create object of Editor

Editor is interface declared inside the SharedPreference class. so that To access Editor class we need to write following syntax, "pref" is object of SharedPreference.

SharedPreferences.Editor edit = pref.edit();


2.2 Put value in SharedPreference

The following data types are supported by the SharedPreferences class:
  • Boolean values
  • Float values
  • Integer values
  • Long values
  • String values
Putting value in SharedPreference using Editor object is very simple, It's same as we are using Map Collection class. Here I put one value of each data type. 

           edit.putBoolean("Boolean_Value", true);
        edit.putFloat("Float_value", 12.5F);
        edit.putInt("Int_value", 500);
        edit.putLong("Long_Value", 1234567890);
        edit.putString("String_Value", "Hello SharedPreference");

SharedPreference is not support to put object inside it but If you can convert your object in String then It is possible. Support you want to store small image in SharedPreference then It is possible using Base64. just convert your image in Base64 and put it as String. but same technique is not possible with object of any class.

2.3 Commit the values

Commit your preferences changes back from this Editor to the SharedPreferences object it is editing. This atomically performs the requested modifications, replacing whatever is currently in the SharedPreferences.

        edit.commit();

Note that when two editors are modifying preferences at the same time, the last one to call commit wins.

If you don't care about the return value and you're using this from your application's main thread, consider using apply() instead.

2.4 Retrieving values


You don’t need an Editor to simply read preferences. Instead, retrieve the SharedPreferences object and use the appropriate method to retrieve a preference by name:


        boolean b = pref.getBoolean("Boolean_Value",false);
        float f = pref.getFloat("Float_Value", 0);
        int i = pref.getInt("Int_Value", 0);
        long l = pref.getLong("Long_Value", 0);
        String s = pref.getString("String_Value", "");


Each of these methods has two parameters: the preference key string and a default value to return if the preference is undefined.

2.5 Remove And Clear

Editor class has two method remove and clear. as name suggest it is used to remove specific key and clear is used to remove all values.

       // Remove value
        edit.remove("Boolean_Value");
        edit.commit();
        
        // Clear All value
        edit.clear();
        edit.commit();

2.5. Full Source Code


package com.kpbird.mysp;

import android.app.Activity;
import android.content.SharedPreferences;
import android.os.Bundle;

public class MySP extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        // fetch object of SharedPreference
        SharedPreferences pref = getSharedPreferences("MyPref"MODE_PRIVATE);
        // Create object of Editor
        SharedPreferences.Editor edit = pref.edit();
        // Put values 
        edit.putBoolean("Boolean_Value"true);
        edit.putFloat("Float_value", 12.5F);
        edit.putInt("Int_value", 500);
        edit.putLong("Long_Value", 1234567890);
        edit.putString("String_Value""Hello SharedPreference");
        // Commit the data
        edit.commit();
        
        
        //Retreive 
        boolean b = pref.getBoolean("Boolean_Value",false);
        float f = pref.getFloat("Float_Value", 0);
        int i = pref.getInt("Int_Value", 0);
        long l = pref.getLong("Long_Value", 0);
        String s = pref.getString("String_Value""");
        
    }
}


3. XML File

When we execute above application, Android will generate xml file named "MyPref.xml" in following path
/data/data/com.kpbird.mysp/shared_prefs/MyPref.xml

XML File content 

<?xml version='1.0encoding='utf-8' standalone='yes' ?>
<map>
<int name="Int_value" value="500" />
<boolean name="Boolean_Value" value="true" />
<long name="Long_Value" value="1234567890" />
<string name="String_Value">Hello SharedPreference</string>
<float name="Float_value" value="12.5" />
</map>

XML File after remove Boolean_Value

<?xml version='1.0encoding='utf-8' standalone='yes' ?>
<map>
<int name="Int_value" value="500" />
<long name="Long_Value" value="1234567890" />
<string name="String_Value">Hello SharedPreference</string>
<float name="Float_value" value="12.5" />
</map>

XML File after clear 

<?xml version='1.0encoding='utf-8' standalone='yes' ?>
<map />


4. SharedPreference In Depth

Above information are general usage and implementation of SharedPreference but this article is for InDepth. I am going to answer following questions.

1. When SharedPreference object create ?
2. Who create object of SharedPreference ?
3. Where getSharedPreference() is implemented ?

SharedPreference is Interface declared in android.content package. Classes and Interface Included in SharedPreference


  1. SharedPerfereance - Interface
  2. Context - Abstract Class
  3. ContextWrapper - Class
  4. Activity - Class
  5. ActivityThread - final Class
  6. ContextImpl - Class

1. SharedPreference : This Interface has methods and two sub/inner Interface Editor and OnSharedPreferenceChangeListener All ShredPreference method are declared in this Interface 


2. Context : This is Abstract Class. In this class getSharedPreference method is declared. getSharedPreference method is public and abstract. It also has static and final integer variable for Mode.
    public static final int MODE_PRIVATE = 0x0000;
    public static final int MODE_WORLD_READABLE = 0x0001;
    public static final int MODE_WORLD_WRITEABLE = 0x0002;

public abstract SharedPreferences getSharedPreferences(String name,int mode);

3. ContextWrapper : This class is child class of Context Class. As name suggest it wrapper all method of Context Class. No real implementation available in this class.  Following method declared in ContextWrapper class.
    public SharedPreferences getSharedPreferences(String name, int mode) {
         return mBase.getSharedPreferences(name, mode);
    }


4. Activity : This class is child class of ContextThemeWrapper and ContextThemeWrapper is child class of ContextWrapper. getSharedPreference method is not implemented int this class. but It has object of ActivityThread. Activity class has method named "attach" Variable for Context, ActivityThread, Configuration, etc.. are pass from system. ActivityThread variable assign to mMainThread variable. attach method is call from "performLunchActivity" method of ActivityThread.

ActivityThread mMainThread;  

final void attach(Context context, ActivityThread aThread,
             Instrumentation instr, IBinder token, int ident,
             Application application, Intent intent, ActivityInfo info,
             CharSequence title, Activity parent, String id,
             Object lastNonConfigurationInstance,
             HashMap<String,Object> lastNonConfigurationChildInstances,
             Configuration config)

5. ActivityThread : ActivityThread is final class.  It has object on ContextImpl class.  ActivityThread has method named "performLunchActivity" in this method object of ContextImpl is create.

          ContextImpl appContext = new ContextImpl();
    appContext.init(r.packageInfo, r.token, this);
    appContext.setOuterContext(activity);
    CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
    Configuration config = new Configuration(mConfiguration);
                

    activity.attach(appContext, this, getInstrumentation(), r.token,r.ident, app, r.intent, r.activityInfo, title, r.parent,r.embeddedID, r.lastNonConfigurationInstance,r.lastNonConfigurationChildInstances, config);


6. ContextImpl : This is child class of Context. ContextImpl class has implementation of all abstract method of Context Class. ContextImpl class has private class which implements SharedPrferenceInterface

private static final class SharedPreferencesImpl implements SharedPreferences 

Here is implementation of getSharedPreferences.


 public SharedPreferences getSharedPreferences(String name, int mode) {
        SharedPreferencesImpl sp;
        File prefsFile;
        boolean needInitialLoad = false;
        synchronized (sSharedPrefs) {
            sp = sSharedPrefs.get(name);
            if (sp != null && !sp.hasFileChangedUnexpectedly()) {
                return sp;
            }
            prefsFile = getSharedPrefsFile(name);
            if (sp == null) {
                sp = new SharedPreferencesImpl(prefsFile, mode, null);
                sSharedPrefs.put(name, sp);
                needInitialLoad = true;
            }
        }

        synchronized (sp) {
            if (needInitialLoad && sp.isLoaded()) {
                // lost the race to load; another thread handled it
                return sp;
            }
            File backup = makeBackupFile(prefsFile);
            if (backup.exists()) {
                prefsFile.delete();
                backup.renameTo(prefsFile);
            }

            // Debugging
            if (prefsFile.exists() && !prefsFile.canRead()) {
                Log.w(TAG, "Attempt to read preferences file " + prefsFile + " without permission");
            }

            Map map = null;
            FileStatus stat = new FileStatus();
            if (FileUtils.getFileStatus(prefsFile.getPath(), stat) && prefsFile.canRead()) {
                try {
                    FileInputStream str = new FileInputStream(prefsFile);
                    map = XmlUtils.readMapXml(str);
                    str.close();
                } catch (org.xmlpull.v1.XmlPullParserException e) {
                    Log.w(TAG, "getSharedPreferences", e);
                } catch (FileNotFoundException e) {
                    Log.w(TAG, "getSharedPreferences", e);
                } catch (IOException e) {
                    Log.w(TAG, "getSharedPreferences", e);
                }
            }
            sp.replace(map, stat);
        }
        return sp;
    }




No comments:

Post a Comment