Android has a concept of shared preferences using which application preferences data can be stored persistently. That means the data or state won’t be lost until the application is uninstalled. The preferences data can be stored as key/value pairs and are available across all the Activities of the given application or can also be restricted to a particular Activity.
SharedPreferences
Using the SharedPreferences interface implementations we can store persistent sets of data in the filesystem. The data will be available across application restarts or even device stop/start. Consider it as a small cave to hold your app’s data mostly related to settings and user preferences. Anything that is slightly more complicated like relational data should still go into the sqlite database or media files into the filesystem.
What's the one thing every developer wants? More screens! Enhance your coding experience with an external monitor to increase screen real estate.
SharedPreferences can be used to store any (and only) primitive data types – booleans, floats, ints, longs and strings – that’ll persist across user sessions (user closes the app and re-opens it). To determine what type of data to store, just think of anything that might require caching for quick usage like username, logged in state, email, high score or level in a game, gender (or other profile info), app-related settings, etc.
Shared Preferences can be stored at 2 levels – activity or application. To get a SharedPreferences object for your activity or application in order to start storing, retrieving and updating data there are two methods:
getSharedPreferences()
– Application-wide preferences file identified by the name passed to it as the first argument.getPreferences()
– Activity-level preferences object where no name is specified as there will be only one file for an Activity.
Here’s how the first one is used:
SharedPreferences pref = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE);
You can store as many shared preferences as you want for your application and all of them will keep on getting saved in the XML file named MyPrefs
which is usually located at /data/data/[package name]/shared_prefs/MyPrefs.xml
. You can browse that in the File Explorer in the DDMS view or if your device is rooted then go ahead and explore it in $ adb shell
.
The image shows my app specific data (including shared preferences) in DDMS. com.pycitup.pyc is my application’s package name.
In case you don’t want to provide a name for app-wide shared preferences then we can use PackageManager.getDefaultSharedPreferences()
(not included in the list of methods above) which uses a default name behind the scenes which is a concatenation of the package name and the string _preferences. Hence the file path will be something like /data/data/com.package.name/shared_prefs/com.package.name_preferences.xml
. This is how you use it:
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
The activity-level method version has to be used like this:
SharedPreferences pref = getPreferences(Context.MODE_PRIVATE);
This version will save its data in /data/data/[package name]/shared_prefs/[Activity Name].xml
.
If you notice we passed an option called Context.MODE_PRIVATE
(can also pass just MODE_PRIVATE
if in an activity or the integer 0
) which basically means that the created shared preferences file will be accessible by the calling application only.
Now let’s go through how to write and read preferences.
Storing Preferences
Once you’ve decided whether you want application preferences or activity one, then you’ll start storing data into it. Once you’ve a valid SharedPreferences
object you call edit()
method on it to fetch a SharedPreferences.Editor
object whose public method will allow us to start writing data to the file. To store primitive data it has various methods like putBoolean()
, putFloat()
, putInt()
, putLong()
and putString()
.
Let’s try storing some values:
SharedPreferences pref = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE); // We need an editor object to make changes SharedPreferences.Editor edit = pref.edit(); // Set/Store data edit.putString("username", "Rishabh"); edit.putBoolean("logged_in", true); // Commit the changes edit.commit();
We stored 2 key-value pairs. This is how our /data/data/[package name]/shared_prefs/MyPrefs.xml
should look like now:
<?xml version='1.0' encoding='utf-8' standalone='yes' ?> <map> <string name="username">Rishabh</string> <boolean name="logged_in" value="true" /> </map>
Notice we had to use the commit()
method to commit our changes. There’s another method to do the same which is apply()
but that is asynchronous and won’t report failures.
Updating Preferences
Updating the preferences is similar to the setting them that we just learnt a bit back. Get the SharedPreferences.Editor object, set values using the put*()
methods and then commit your changes.
SharedPreferences pref = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE); // We need an editor object to make changes SharedPreferences.Editor edit = pref.edit(); // Set/Store data edit.putString("username", "CodeTheory"); edit.putBoolean("logged_in", false); // Commit the changes edit.commit();
Same code but different values. The new XML contents looks like this:
<?xml version='1.0' encoding='utf-8' standalone='yes' ?> <map> <boolean name="logged_in" value="false" /> <string name="username">CodeTheory</string> </map>
Retrieving Preferences
Fetching the preferences is done directly on the SharedPreferences
object. So SharedPreferences.Editor
is not required. There are several `get` methods for this job like getBoolean()
, getFloat()
, getInt()
, getLong()
and getString()
. All of them accept two arguments where the first is the name of the key while the second non-optional one is the default value to return if the preference does not exists (is undefined).
SharedPreferences pref = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE); String username = pref.getString("username", ""); boolean logged_in = String.valueOf(pref.getBoolean("logged_in", false); Log.d(TAG, username); Log.d(TAG, String.valueOf(logged_in));
The code logged CodeTheory
and false
in separate lines.
Deleting Preferences
Deleting a particular preference is super simple. Just call the remove()
method and commit your changes.
// Remove a particular key pref.remove("username"); // Commit changes pref.commit();
You can also clear()
the entire remaining preferences from your storage.
Preferences Listener
We can listen to changes in the preferences by passing a SharedPreferences.OnSharedPreferenceChangeListener
listener to registerOnSharedPreferenceChangeListener
. Here’s how to do that:
SharedPreferences pref = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE); pref.registerOnSharedPreferenceChangeListener(new SharedPreferences.OnSharedPreferenceChangeListener() { @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { Log.d(TAG, "The key '" + key + "' was changed"); } }); // Any further code that modifies, add, removes a key will call onSharedPreferenceChanged method
The onSharedPreferenceChanged()
method will be called whenever a particular key is added, changed or removed. Note: At times if this code doesn’t work then try saving the OnSharedPreferenceChangeListener
object into a variable or an instance member to hold a reference to it. This SO answer explains it all.
Bonus
You can always check if a particular preference exists (is set) or not using the contains()
method on the SharedPreferences object like this:
boolean exists = pref.contains("username"); // true
We can write a set of Strings to our preferences using the putStringSet()
method. This is how we can do it:
SharedPreferences pref = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE); // We need an editor object to make changes SharedPreferences.Editor edit = pref.edit(); // Set/Store data HashSet<String> set = new HashSet<String>(); set.add("Rich Dad Poor DaD"); set.add("The Cold Steel"); set.add("Steve Jobs Biography"); edit.putStringSet("books", set); // Commit the changes edit.commit();
Here’s the XML representation (contents):
<?xml version='1.0' encoding='utf-8' standalone='yes' ?> <map> <set name="books"> <string>Steve Jobs Biography</string> <string>The Cold Steel</string> <string>Rich Dad Poor DaD</string> </set> </map>
Using getStringSet()
you can get the Set
data back.
Usage
You might be wondering when to use this data storage option. A few cases that I can think off the bat are:
- Game’s high score and current level.
- Persisting user session post login in your application where the session data contains username, email, gender, etc.
- Applications like WhatsApp have their Settings/Preferences section where you can set the notification ring tone or status message and other app-specific options to modify the behaviour or features. This can be achieved with SharedPreferences easily.
Android Preference APIs/Objects
When building a full-fledged settings/preferences section for the user in your app (somewhat similar to the Android’s Settings app), instead of building the user interface with your own custom View objects and then wiring up the functionality by writing some backend logic in the Activity class, we can use Preference
objects in conjunction with PreferenceActivity
to quickly build the user interface. Along with that the other major advantage of this approach is that, saving preferences is done behind the scenes using SharedPreferences and the integration of the UI with the updated value to set the correct state of various controls (checkbox for example) is also handled automatically. Apart from that this entire framework has its own simple APIs for quick customizations.
The comprehensive Android settings guide has all the information with regards to this.
Conclusion
So we explored the SharedPreferences data storage option which eases some sort of caching by storing super simple and quick key/value pairs (in XML files behind the scenes). Remember for more complicated data like relational data or files there are other options like saving in sqlite or the filesystem.