Using preferences

When moving from Activity to Activity in Android it is very handy to be able to save some global application state in a SharedPreferences object. Here we will discuss how you can set data into a preferences object and how you can later retrieve it. Also, we will discuss how to make preferences private to your application or accessible to other applications on the same device.

5.1.1 Working with SharedPreferences

You access a SharedPreferences object through the Context you are working in. Many Android classes have a reference to, or themselves extend from, Context. For example, Activity and Service both extend Context.

Context includes a getSharedPreferences(String name, int accessMode) method that allows you to get a preferences handle. The name you specify indicates the file that backs the preferences you are interested in. If no such file exists when you try to get preferences, one is automatically created using the passed-in name. The access mode refers to what permissions you want to allow.

Listing 5.1 is an example Activity that demonstrates allowing the user to enter input and then storing that data through SharedPreferences objects with different access modes.

Listing 5.1 Storing SharedPreferences using different modes package com.msi.manning.chapter5.prefs; // imports omitted for brevity public class SharedPrefTestInput extends Activity {

public static final String PREFS_PRIVATE = "PREFS_PRIVATE"; public static final String PREFS_WORLD_READ = "PREFS_WORLD_READABLE"; public static final String PREFS_WORLD_WRITE = "PREFS_WORLD_WRITABLE"; public static final String PREFS_WORLD_READ_WRITE = "PREFS_WORLD_READABLE_WRITABLE";

public static final String KEY_PRIVATE = "KEY_PRIVATE" ; public static final String KEY_WORLD_READ = "KEY_WORLD_READ" ; public static final String KEY_WORLD_WRITE = "KEY_WORLD_WRITE" ; public static final String KEY_WORLD_READ_WRITE = "KEY_WORLD_READ_WRITE";

. . . view element variable declarations omitted for brevity private SharedPreferences prefsPrivate; private SharedPreferences prefsWorldRead; private SharedPreferences prefsWorldWrite; private SharedPreferences prefsWorldReadWrite;

@Override public void onCreate(Bundle icicle) {

Declare

SharedPreferences variables view inflation omitted for brevity this.button.setOnClickListener(new OnClickListener() { public void onClick(final View v) { boolean valid = validate(); if (valid) {

prefsPrivate = getSharedPreferences( SharedPrefTestInput. PREFS_PRIVATE, Context. MODE_PRIVATE) ; <

prefsWorldRead =

getSharedPreferences(

SharedPrefTest Input. PREFS_WORLD_READ, Context. MODE_WORLD_READABLE) ; <

prefsWorldWrite = Use different getSharedPreferences ( modes

SharedPrefTest Input. PREFS_WORLD_WRITE, Context. MODE_WORLD_WRITEABLE) ; <

prefsWorldReadWrite = getSharedPreferences( SharedPrefTest Input. PREFS_WORLD_READ_WRITE, Context. MODE_WORLD_READABLE

Editor prefsPrivateEditor =

prefsPrivate.edit(); Editor pref sWorldReadEditor =

prefsWorldRead.edit(); Editor pref sWorldWriteEditor =

prefsWorldWrite.edit(); Editor pref sWorldReadWriteEditor = prefsWorldReadWrite.edit() ;

prefsPrivateEditor.putString(

SharedPrefTestInput. KEY_PRIVATE,

Use Context. getShared-Preferences for references

E Get SharedPreferences

Editor inputPrivate.getText.toString()); prefsWorldReadEditor.putString(

SharedPrefTestInput. KEY_WORLD_READ, inputWorldRead.getText().toString());

prefsWorldWriteEditor.putString(

SharedPrefTest Input. KEY_WORLD_WRITE, inputWorldWrite.getText().toString()); prefsWorldReadWriteEditor.putString(

SharedPrefTest Input. KEY_WORLD_READ_WRITE, inputWorldReadWrite.getText().toString())

F Store values with editor prefsPrivateEditor.commit(); prefsWorldReadEditor.commit(); prefsWorldWriteEditor.commit(); prefsWorldReadWriteEditor.commit();

Intent intent = new Intent(SharedPrefTestInput.this, SharedPrefTestOutput.class); startActivity(intent);

Commit changes with editoreferences variables

. . . validate omitted for brevity

Once you have a SharedPreferences variable O, you may assign a reference through the Context ©. Note that for each SharedPreferences object we are getting, we are using a different constant value for the access mode, and in some cases we are even adding modes (modes are of int type) ©. Modes specify whether or not the preferences should be private, world readable, world writable, or a combination.

After you have preferences, you can then get an Editor handle in order to start manipulating values ©. With the Editor you can set String, boolean, float, int, and long types as key-value pairs ©. This limited set of types can be restrictive, and it is why we extended the Context in chapter 3 to store some application state in the form of a complex object rather than using preferences. Even with this restriction, though, often preferences are adequate, and as you can see they are simple to use.

After you have stored data with an Editor, which creates an in-memory Map, you have to remember to call commit() to persist it to the preferences backing file ©. After data is committed, you can get it from a SharedPreferences object even easier than storing it. Listing 5.2 is an example Activity from the same application (same package) that gets and displays the data that was stored in listing 5.1.

Listing 5.2 Getting SharedPreferences data stored in the same application package com.msi.manning.chapter5. prefs ; // imports omitted for brevity public class SharedPrefTestOutput extends Activity {

. . . view element variable declarations omitted for brevity private SharedPreferences prefsPrivate; private SharedPreferences prefsWorldRead; private SharedPreferences prefsWorldWrite; private SharedPreferences prefsWorldReadWrite;

. . . onCreate omitted for brevity

Declare

SharedPreferences variables

@Override public void onStart() { super.onStart(); this.prefsPrivate = getSharedPreferences(SharedPrefTestInput.PREFS_PRIVATE, Context. MODE_PRIVATE) ; this.prefsWorldRead = getSharedPreferences(SharedPrefTestInput.PREFS_WORLD_READ, Context.MODE_WORLD_READABLE) ; this.prefsWorldWrite = getSharedPreferences (SharedPrefTestInput. PREFS_WORLD_WRITE, Context. MODE_WORLD_WRITEABLE) ; «

this .prefsWorldReadWrite = Assign C

SharedPreferences getSharedPreferences ( variables

SharedPrefTestInput. PREFS_WORLD_READ_WRITE, Context. MODE_WORLD_READABLE + Context .MODE_WORLD_WRITEABLE) ;

this.outputPrivate.setText(this.prefsPrivate.getString(

SharedPrefTestInput. KEY_PRIVATE, "NA" ) ) ; this . outputWorldRead. setText (this .pref sWorldRead.getString ( Get values

SharedPrefTestInput. KEY_WORLD_READ, "NA")); <1—

this.outputWorldWrite.setText(this.prefsWorldWrite.getString(

SharedPrefTestInput. KEY_WORLD_WRITE, "NA")); <

this.outputWorldReadWrite.setText(this.prefsWorldReadWrite.getString( SharedPrefTestInput. KEY_WORLD_READ_WRITE, <

To get SharedPreferences values that we have previously stored, we again declare variables B and assign references C. Once these are in place, we can simply get values using methods such as getString(String key, String default) G.

So, as you can see, setting and getting preferences is very straightforward. The only potential flies in the ointment are the access modes, which we will focus on next.

Preference access permissions

SharedPreferences can be opened or created with any combination of several Context mode constants. Because these values are int types, they can be added together, as we did in listings 5.1 and 5.2, to combine permissions. The supported mode constants are as follows:

■ Context.MODE_PRIVATE (value 0)

■ Context.MODE_WORLD_READABLE (value 1)

■ Context.MODE_WORLD_WRITEABLE (value 2)

These modes allow you to finely tune who has access to what preference. If we take a look at the filesystem on the emulator, after having created SharedPreferences objects (which themselves create XML files to persist the data), we can see how this works using a Linux-based filesystem.

Figure 5.1 is a screen shot of the Android Eclipse plug-in File Explorer view; it shows the Linux-level permissions for the SharedPreferences XML files that were created in listing 5.1 (these were automatically created for us when we used SharedPreferences).

The quick and dirty version of how Linux file permissions work is that each file (or directory) has a type and three sets of permissions represented by a drwxrwxrwx notation. The first character indicates the type (d means directory, - means regular file type, and symbolic links and other things can be represented using the type as well). After the type, the three sets of rwx represent read, write, and/or execute permissions for user, group, and other, in that order. So looking at this notation we can tell which files are accessible by the user they are owned by, or by the group they belong to, or by other.

Directories with the other x permission

Directory permissions can be confusing. The important thing to remember with regard to Android, though, is that each package directory is created with the other x permission. This means anyone can search and list the files in the directory. This, in turn, means that Android packages have directory-level access to one another's files—from there the file-level access determines file permissions.

SharedPreferences XML files are placed in the /data/data/PACKAGE_NAME/ shared_prefs path on the filesystem. Every application or package (each .apk file) has its own user ID (unless you use sharedUserId in the manifest, which allows you to share the user ID, but that's a special exception). When an application creates files (including SharedPreferences), they are owned by that application's user ID. To allow other applications to access these files, the other permissions have to be set (as

T k3 com.msi.manning.chapterS.prefs

2008

-03

-12

13:40

drwxrwx—x

Y sharcd.prefs

2003

-03

-12

13:41

drwKrwx--x

Ë PREFS_ PRIVATE.xml

114

2008

-03

-12

13:41

rw rw----

¡1 PREFS_WORLD_READABLE.xml

117

2008

-03

-12

13:41

-rw-rw-r—

B PREF£_WORLD_R£ADABLE_WRITABLE.xml

126

2003

-03

-12

13:41

-rw-rw-rw-

i PREFS.WORLD_WRITABLE.xml

119

2008

-03

-12

13:41

-rw-rw--w-

com.other.manning.chapters.prefi

2008

-03

-12

13:42

d rwx rwx—x

& downloùd

2008

-03

-12

13:37

drwxrvjxrwx

Figure 5.1 The Android File Explorer view showing preferences file permissions

Figure 5.1 The Android File Explorer view showing preferences file permissions shown in figure 5.2, where one of our preferences files has no outside permissions, one of our files is world-readable, one is world-readable and -writable, and one is world-writable).

The tricky part with getting access to the files of one application from another, even when they have accessible permissions, is the starting path. The path is built from the Context. So, to get files from another application you have to know and use that application's Context. An example of this is shown in listing 5.3, where we get the SharedPreferences we set in listing 5.1 again, this time from a different application (different .apk and different package).

Listing 5.3 Getting SharedPreferences data stored in a different application package com. other .manning. chapter5 .prefs; <—O Use a different package . . . imports omitted for brevity public class SharedPrefTestOtherOutput extends Activity {

. . . constants and variable declarations omitted for brevity

. . . onCreate omitted for brevity

@Override public void onStart() { super.onStart();

Context otherAppsContext = null; try {

otherAppsContext = createPackageContext("com.msi.manning.chapter5.prefs",

Context. MODE_WORLD_WRITEABLE) ; <-1

} catch (NameNotFoundException e) { I Get another

// log and or handle C application's context

this.prefsPrivate = otherAppsContext.getSharedPreferences( SharedPrefTestOtherOutput.PREFS_PRIVATE, 0); <

this.prefsWorldRead = otherAppsContext.getSharedPreferences( SharedPrefTestOtherOutput. PREFS_WORLD_READ, 0); <H

this.prefsWorldWrite = Use ©

otherAppsContext .getSharedPreferences ( °therAppsC°ntext

SharedPrefTestOtherOutput. PREFS_WORLD_WRITE, 0); this.prefsWorldReadWrite = otherAppsContext.getSharedPreferences( SharedPrefTestOtherOutput. PREFS_WORLD_READ_WRITE, 0);

this.outputPrivate.setText(

this.prefsPrivate.getString( SharedPrefTestOtherOutput. KEY_PRIVATE, "NA" ) ) ; this.outputWorldRead.setText( this.prefsWorldRead.getString( SharedPrefTestOtherOutput. KEY_WORLD_READ, "NA" ) ) ; this.outputWorldWrite.setText( this.prefsWorldWrite.getString( SharedPrefTestOtherOutput. KEY_WORLD_WRITE, "NA" ) ) ;

this.outputWorldReadWrite.setText( this.prefsWorldReadWrite.getString( SharedPrefTestOtherOutput.KEy_WOÄLD_ÄEAD_WHITE/"NA"));

To get to the SharedPreferences one application has defined from another application in a different package O, we must use the createPackageContext(String context-Name, int mode) method ©. Once we have a reference to the other application's Context, we can use the same names for the SharedPreferences objects the other application created (we do have to know the names) to access those preferences G.

With these examples we now have one application that sets and gets SharedPreferences and a second application (in a different package, with a different .apk file) that gets the preferences set by the first. The composite screen shot shown in figure 5.2 demonstrates what this looks like (where NA is the preferences we could not access from the second application, due to permissions).

Figure 5.2 Two separate applications getting and setting

SharedPreferences

Figure 5.2 Two separate applications getting and setting

SharedPreferences

The way SharedPreferences are backed by XML files on the Android filesystem and use permission modes leads us to the next method of storing and retrieving data, the filesystem itself.

Was this article helpful?

0 0

Responses

  • AILA
    How to edit android .prefs files?
    7 years ago

Post a comment