What You’ll Learn in This Hour
Using SharedPreferences
to store data
Creating a PreferencesFragment
Generating a PreferencesActivity
SharedPreferences(android.content.SharedPreferences)
is a class that provides the ability to store data in key-value pairs. This data can be set and retrieved in any activity. Android provides a robust Preferences API for user settings that use SharedPreferences
as the underlying data store. In this hour, you learn how to use SharedPreferences
for a simple app setting. You’ll also develop a robust user interface for user settings in the app.
SharedPreferences
provide a mechanism for storing and retrieving data as key-value pairs. In this hour, you create a single activity app to demonstrate how to use preferences. The Hour11App project that accompanies this hour contains this source code.
In this app, the goal is to show a message the first time the app is used. To do that, you need to know that the app has been run and you need to retain that information.
Logically, you can use a boolean value that is set to true the first time the user runs the app. After that, the value is false. Use the string "firstUse"
as a key. Prefacing the key with the key with the package name for the app is common practice. If the package name is com.example.app
, then the key would be "com.example.app.firstUse"
. That string can be stored in a values resource file or in another definition file within the app. You define the key and the possible values are true and false.
The key-value pair you use must be retained across uses of the app. The data will be put into a file that is defined with a call to the getSharedPreferences()
method. This method takes two parameters, name
and mode
. name
is the filename where the key/value pairs are stored. It is a string and should be start with the package name. The mode parameters represent the type of file storage that should be used. This is typically Context.MODE_PRIVATE
indicating that the settings are private. Others options are Context.MODE_WORLD_READABLE
and Context.MODE_WORLD_WRITABLE
, but these values were deprecated in API level 17. In standard use, app settings and shared preferences are private, and these settings were deprecated to help enforce that privacy.
Listing 11.1 shows a snippet of code that defines string constants for the SETTINGS
and FIRST_USE
.SETTINGS
is used as the name parameter in the getSharedPreferences()
method on line 3 and FIRST_USE
is used for the key value on line 5.
On line 4, a SharedPreferences.Editor
is instantiated. A SharedPreferences.Editor
is used to insert or update values. On line 6, commit()
is called to store the values.
1: public static final String SETTINGS = "com.bffmedia.hour11app.settings";
2: public static final String FIRST_USE = "com.bffmedia.hour11app.firstUse";
3: SharedPreferences preferences = getSharedPreferences(SETTINGS, MODE_PRIVATE);
4: Editor edit = preferences.edit();
5: edit.putBoolean(FIRST_USE, false);
6: edit.commit();
On line 3 of Listing 11.1, a SharedPreferences
object called preferences
was created. To read preferences
, you use the appropriate get
method—in this case, getBoolean()
. The first parameter is the key to read and the second parameter is a default value. If the key FIRST_USE
has not been set, the returned value for the boolean firstUse
would be true:
boolean firstUse = preferences.getBoolean(FIRST_USE, true);
Listing 11.2 shows how the firstUse
variable is read and set within an activity’s onCreate()
method to display a message on the first time use of the app. On line 2, the method preferences.getBoolean()
is called with the key FIRST_USE
and a default setting of true
. The first time this code runs, there will be no value associated with the key FIRST_USE
, and the variable firstUse
will be set to the default value of true
. Lines 4–6 show a Toast
message and lines 7–9 set the FIRST_USE
value to false
. This is the implementation for showing a message on the first use of the app.
1: SharedPreferences preferences = getSharedPreferences(SETTINGS, MODE_PRIVATE);
2: boolean firstUse = preferences.getBoolean(FIRST_USE, true);
3: if (firstUse){
4: Toast helloMessage = Toast.makeText(getApplicationContext(),
5: "Hello First Time User",Toast.LENGTH_LONG);
6: helloMessage.show();
7: Editor editor = preferences.edit();
8: editor.putBoolean(FIRST_USE, false);
9: editor.commit();
10: }
In the case of first-time use flag, SharedPreferences
serves as a storage mechanism for key-value pairs. Nothing is directly entered by the user. You can also use SharedPreferences
for timestamps or for simple tracking.
You can count the number of times a user takes a certain action in your app and store the value in SharedPreferences
. At a certain threshold, you might prompt the user to rate your app or take some other action.
In another example, you can use the system time to set time stamps and then check for elapsed time. Note that if a timestamp is critical to your application, then do not use the system time because it can be changed in the Settings application. The following code saves the current time in milliseconds as a long
. This can be read in the future and compared to the current time as one way to determine elapsed time since the value was saved:
public static final String TIMESTAMP = "com.bffmedia.hour11app.timeStamp";
long timeStamp = System.currentTimeMillis();
SharedPreferences.Editor editor = preferences.edit();
editor.putLong(TIMESTAMP, timeStamp);
editor.commit();
Common types can be stored and retrieved from SharedPreferences
. There are methods for getBoolean()
, getFloat()
, getInt()
, getLong()
, getString()
, and getStringSet()
.
In addition, there is a getAll()
method and a contains()
method. The getAll()
method gets all key-value pairs as a Map
where the key is a string and the value is whatever was put into SharedPreferences
. The contains()
method takes a string as a parameter. The string is the key to check. The contains()
method returns true if the key is contained in SharedPreferences
.
Note: Listening for Changes in SharedPreferences
SharedPreferences
also includes a registerOnSharedPreferenceChangeListener()
method. This allows your app to listen for changes to preferences and react if necessary. There is also a corresponding unregisterOnSharedPreferenceChangeListener()
method.
SharedPreferences
implements a data store for key-value pairs. Android uses the SharedPreferences
data store together with a set of Preference APIs to provide a robust way to implement user settings.
You can use either a PreferenceActivity(android.preference.PreferenceActivity)
or a PreferenceFragment(android.preference.PreferenceFragment)
as the user interface for settings. The user changes the settings, and the SharedPreferences
data store updates.
The Preference
class extends to provide subclasses to handle specific types of settings. Each subclass of Preference
includes properties for the settings and a user interface for displaying the preference. For example, a CheckBoxPreference (android.preference.CheckBoxPreference)
presents a checkbox and a message to the user. The user may check or uncheck the checkbox. The value associated with a CheckBoxPreference
is a boolean with true
indicating that the checkbox is checked.
When creating a PreferenceFragment
or PreferenceActivity
, you use an XML file to define the preferences. This is not a res/layout XML file. Layout files define views. In this case, the XML file defines preferences. You put the preference XML file in a res/xml folder. Use the filename preferences.xml to create a PreferenceFragment
.
Listing 11.3 shows the entire SettingsFragment
class. SettingsFragment
extends PreferenceFragment
and implements onSharedPreferenceChangeListener
. On line 12, within the onCreate()
method, a call is made to addPreferencesFromResource()
, which reads the data in the R.xml.preferences
file and shows the appropriate user settings.
The onSharedPreferenceChangeListener
is implemented. The registerOnSharedPreferenceChangeListener()
method is called on lines 17–18 in the fragment’s onResume()
method. It is unregistered in the onPause()
method on lines 23–24. When a change is detected in the onSharedPreferenceChanged()
method, you just log the passed key. This allows you to see when a change occurs using LogCat
.
1: package com.bffmedia.hour11app;
2: import android.content.SharedPreferences;
3: import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
4: import android.os.Bundle;
5: import android.preference.PreferenceFragment;
6: import android.util.Log;
7: public class SettingsFragment extends PreferenceFragment
8: implements OnSharedPreferenceChangeListener {
9: @Override
10: public void onCreate(Bundle savedInstanceState) {
11: super.onCreate(savedInstanceState);
12: addPreferencesFromResource(R.xml.preferences);
13: }
14: @Override
15: public void onResume() {
16: super.onResume();
17: getPreferenceScreen().getSharedPreferences().
18: registerOnSharedPreferenceChangeListener(this);
19: }
20: @Override
21: public void onPause() {
22: super.onPause();
23: getPreferenceScreen().getSharedPreferences().
24: unregisterOnSharedPreferenceChangeListener(this);
25: }
26: @Override
27: public void onSharedPreferenceChanged(SharedPreferences
28: sharedPreferences, String key) {
29: Log.d("Settings", key);
30: }
31: }
The contents of the preferences.xml
file determine what the SettingsFragment
looks like when displayed to the user.
Listing 11.4 shows a sample preferences.xml file. A PreferenceScreen
contains a CheckBoxPreference
. Lines 4–7 include the attributes for the preference. The key on line 4 stores the associated data. The title and summary on lines 5 and 6 are used when displaying this preference. The default value on line 7 indicates the initial setting for the preference. With a default value of false
, the checkbox is not enabled.
Figure 11.1 shows the SettingsFragment
when the preference.xml file defined in Listing 11.4 is used.
1: <?xml version="1.0" encoding="utf-8"?>
2: <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
3: <CheckBoxPreference
4: android:key="hires"
5: android:title="Hi-Res Images"
6: android:summary="Show high quality images. These take longer to load"
7: android:defaultValue="False" />
8: </PreferenceScreen>
In the app, the SettingsFragment
is used in a SettingsActivity
. The SettingsActivity
just uses setContentView()
to display a layout that contains the SettingsFragment
. The activity_settings.xml layout file is shown here:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment android:name="com.bffmedia.hour11app.SettingsFragment"
android:id="@+id/settings_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
The onCreateMenuOptions()
and onOptionsItemSelected()
were implemented in the MainActivity
to show Settings in the overflow menu.
Preference types including the CheckBoxPreference
are listed in Table 11.1.
The following sections show additional simple preferences and then look at how you can handle implementing many preferences using titles and subscreens.
Listing 11.5 shows an additional CheckBoxPreference
and a ListPreference(android.preference.ListPreference)
. The CheckBoxPreference
indicates whether or not the user likes pie. The ListPreference
lets the user indicate which type of pie he or she likes. Perhaps the app will show hi-res images of pies.
In the ListPreference
definition on lines 7–15, you should note several things. Line 8 indicates a dependency on "pie"
, which is the CheckBoxPreference
defined in lines 1–5. That means that the ListPreference
will not be available if the CheckBoxPreference
with the key "pie"
is not selected. Figure 11.2 shows the two states for this. Line 15 specifies a defaultValue
.
1: <CheckBoxPreference
2: android:key="pie"
3: android:title="Pie"
4: android:summary="Like Pie"
5: android:defaultValue="true" />
6:
7: <ListPreference
8: android:dependency="pie"
9: android:key="pie_type"
10: android:title="Pie Type"
11: android:summary="Preferred pie type for eating"
12: android:dialogTitle="Type of Pie"
13: android:entries="@array/pie_array"
14: android:entryValues="@array/pie_array"
15: android:defaultValue="apple" />
The ListPreference
refers to the @array/pie_array
that is defined in the res/values/Strings.xml file. Entries
are what display to the user. EntryValues
are the corresponding values. In this case, the name of the pies is both the entries
and the entryValues
. In other cases, the display name might be different than the entryValue
. For example, a country code might be stored while a country name displays.
Figure 11.3 shows the ListPreference
display.
The third basic type of preference is the EditTextPreference (android.preference.EditTextPreference)
. The EditTextPreference
is for free-form entry from the user. The following XML snippet defines an EditTextPreference
. Figure 11.4 shows the resulting display: the settings page and the dialog to accept additional data.
<EditTextPreference
android:key="more_info"
android:title="More Info"
android:summary="More about pies"
android:defaultValue="" />
Adding titles to organize content for settings is easy. Each set of related content that appears under a title should be wrapped in the XML in a PreferenceCategory(android.preference.PreferenceCategory)
with a title. The following snippet shows a sample PreferenceCategory
. The list of preferences is added to the PreferenceCategory
. Figure 11.5 shows the result.
<PreferenceCategory android:title="Pie Info">
...add preferences here
</PreferenceCategory>
This example uses titles to organize the content on the preference screen. Another way to organize the content is to use subscreens. With a subscreen, a description of the preference is shown on the main preferences page, but the user selects options on a separate subscreen.
You define subscreens by nesting a PreferenceScreen(android.preference.PreferenceScreen)
element in the XML. Listing 11.4 uses one PreferenceScreen
to contain all the preferences. You can add one or more additional PreferenceScreens
. Listing 11.6 shows a snippet of the preferences.xml file for a subscreen. The XML shown in Listing 11.6 is nested within another PreferenceScreenElement
.
Figure 11.6 shows the resulting settings page. The title appears on the main settings page. Selecting that title makes the subscreen appear.
1: <PreferenceScreen
2: android:key="second_preferencescreen"
3: android:title="Second Screen of Settings">
4: <EditTextPreference
5: android:key="extraA"
6: android:title="More Data"
7: android:summary="Another EditTextPreference"
8: android:defaultValue="" />
9: <EditTextPreference
10: android:key="ExtraB"
11: android:title="Even More Info"
12: android:summary="What more can we say"
13: android:defaultValue="" />
14: </PreferenceScreen>
Reading these preferences is like reading from SharedPreferences
, but you use PreferenceManager
to get the default shared preferences:
SharedPreferences sharedPref =
PreferenceManager.getDefaultSharedPreferences(getActivity());
You can then retrieve data with get
s from SharedPreferences
; for example, sharedPref.getString("pie_type", "")
.
A factor in using the PreferenceFragment
is that some PreferenceActivity
methods are being deprecated. Figure 11.7 shows the message from the online documentation regarding this issue.
On the other hand, Eclipse provides a convenient method for creating a SettingsActivity
.
Figure 11.9 shows the generated SettingsActivity
. You can modify it for your needs.
In this hour, you used the SharedPreference
data store to save key-value pairs. In the example, you used SharedPreferences
to show a message only on the first-time app use. You learned about Preferences
and how to make a settings page using XML to define preferences including CheckBoxPreference
, ListPreference
, and EditTextPreference
. You saw how to add titles and subscreens to preferences. SharedPreferences
enable simple data storage, and the Preferences API is a powerful way to implement user settings
Q. Are settings required in an app?
A. No, but using them is often a good idea. The style guide for settings provides additional information on when you should add settings. The specific guidance is: avoid the temptation to make everything a setting. (See http://developer.android.com/design/patterns/settings.html.)
1. What are common types of preferences?
2. What does dependency mean in a preferences file?
3. If you are retrieving a boolean value from sharedPreferences
and you want the value to be false
if there was no value in sharedPreferences
, how would you do that?
1. Common preferences are CheckBoxPreference
, ListPreference
, and EditTextPreference
. Table 11.1 lists additional preferences.
2. When a dependency is included for a preference, the dependent preference will not be available unless the primary preference is set. You saw this in the pie example.
3. You indicate a default value on reading the data. For example, in this statement, the default value for FIRST_USE
is true
: preferences.getBoolean(FIRST_USE, true);
.
Settings depend on the app you are building. Consider an app that you are building or want to build and define a list of settings. Implement those settings in either a PreferenceFragment
or a PreferenceActivity
.
3.148.104.124