• No se han encontrado resultados

HIPOGLICEMIA NEONATAL:

In document CUADERNO PROf Loreto Vargas (página 32-37)

Android provides several ways to store and share data, including access to the file- system, a local relational database through SQLite, and a preferences system that allows you to store simple key/value pairs within applications. In this chapter, we’ll start with preferences and you’ll create a small sample application to exercise those concepts. From there, you’ll create another sample application to examine using the filesystem to store data, both internal to the application and external using the platform’s Secure Digital (SD) card support. You’ll also see how to create and access a database.

Beyond the basics, Android also allows applications to share data through a clever URI-based approach called a ContentProvider. This technique combines several other Android concepts, such as the URI-based style of intents and the

This chapter covers

 Storing and retrieving data with SharedPreferences

 Using the filesystem

 Working with a SQLite database

131

Using preferences

Cursor result set seen in SQLite, to make data accessible across different applications. To demonstrate how this works, you’ll create another small sample application that uses built-in providers, then we’ll walk through the steps required to create your own ContentProvider.

We’ll begin with preferences, the simplest form of data storage and retrieval Android provides.

5.1

Using preferences

If you want to share simple application data from one Activity to another, use a SharedPreferences object. You can save and retrieve data, and also choose whether 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 your current Context, such as the Activity or Service. Context defines the method getSharedPreferences(String name, intaccessMode) that allows you to get a preferences handle. The name you specify will be the name for the file that backs these preferences. If no such file exists when you try to get preferences, one is automatically created. The access mode refers to what permissions you want to allow.

The following listing demonstrates allowing the user to input and store data through SharedPreferences objects with different access 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) { ... view inflation omitted for brevity

button.setOnClickListener(new OnClickListener() { public void onClick(final View v) {

boolean valid = validate();

Listing 5.1 Storing SharedPreferences using different modes

Declare

SharedPreferences variables

if (valid) { prefsPrivate = getSharedPreferences( SharedPrefTestInput.PREFS_PRIVATE, Context.MODE_PRIVATE); prefsWorldRead = getSharedPreferences( SharedPrefTestInput.PREFS_WORLD_READ, Context.MODE_WORLD_READABLE); prefsWorldWrite = getSharedPreferences( SharedPrefTestInput.PREFS_WORLD_WRITE, Context.MODE_WORLD_WRITEABLE); prefsWorldReadWrite = getSharedPreferences( SharedPrefTestInput.PREFS_WORLD_READ_WRITE, Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE); Editor prefsPrivateEditor = prefsPrivate.edit(); Editor prefsWorldReadEditor = prefsWorldRead.edit(); Editor prefsWorldWriteEditor = prefsWorldWrite.edit(); Editor prefsWorldReadWriteEditor = prefsWorldReadWrite.edit() prefsPrivateEditor.putString( SharedPrefTestInput.KEY_PRIVATE, inputPrivate.getText.toString()); prefsWorldReadEditor.putString( SharedPrefTestInput.KEY_WORLD_READ, inputWorldRead.getText().toString()); prefsWorldWriteEditor.putString( SharedPrefTestInput.KEY_WORLD_WRITE, inputWorldWrite.getText().toString()); prefsWorldReadWriteEditor.putString( SharedPrefTestInput.KEY_WORLD_READ_WRITE, inputWorldReadWrite.getText().toString()); prefsPrivateEditor.commit(); prefsWorldReadEditor.commit(); prefsWorldWriteEditor.commit(); prefsWorldReadWriteEditor.commit(); Intent intent = new Intent(SharedPrefTestInput.this, SharedPrefTestOutput.class); startActivity(intent); } } }); }

. . . validate omitted for brevity }

After you have a SharedPreferences variable

B

, you can acquire a reference through the Context

C

. Note that for each SharedPreferences object we get, we use

Use Context.getShared Preferences for references

C

Use different modes

D

Get SharedPreferences editor

E

Store values with editor

F

Persist changes

G

133

Using preferences

a different constant value for the access mode, and in some cases we also add modes

D

. We repeat this coding for each mode we retrieve. Modes specify whether the pref- erences should be private, world-readable, or world-writable.

To modify preferences, you must get an Editor handle

E

. With the Editor, you can set String, boolean, float, int, and long types as key/value pairs

F

. This limited set of types can be restrictive, but often preferences are adequate, and they’re simple to use.

After storing with an Editor, which creates an in-memory Map, you have to call commit() to persist it to the preferences backing file

G

. After data is committed, you can easily get it from a SharedPreferences object. The following listing gets and dis- plays the data that was stored in listing 5.1.

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

@Override

public void onStart() { super.onStart(); prefsPrivate = getSharedPreferences(SharedPrefTestInput.PREFS_PRIVATE, Context.MODE_PRIVATE); prefsWorldRead = getSharedPreferences(SharedPrefTestInput.PREFS_WORLD_READ, Context.MODE_WORLD_READABLE); prefsWorldWrite = getSharedPreferences(SharedPrefTestInput.PREFS_WORLD_WRITE, Context.MODE_WORLD_WRITEABLE); prefsWorldReadWrite = getSharedPreferences( SharedPrefTestInput.PREFS_WORLD_READ_WRITE, Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE); outputPrivate.setText(prefsPrivate.getString( SharedPrefTestInput.KEY_PRIVATE, "NA")); outputWorldRead.setText(prefsWorldRead.getString( SharedPrefTestInput.KEY_WORLD_READ, "NA")); outputWorldWrite.setText(prefsWorldWrite.getString( SharedPrefTestInput.KEY_WORLD_WRITE, "NA")); outputWorldReadWrite.setText(prefsWorldReadWrite.getString( SharedPrefTestInput.KEY_WORLD_READ_WRITE, "NA")); } }

Listing 5.2 Getting SharedPreferences data stored in the same application

B

To retrieve previously stored values, we again declare variables and assign references. When these are in place, we can get values using methods such as getString(String key, String default)

B

. The default value is returned if no data was previously stored with that key.

Setting and getting preferences is straightforward. Access modes, which we’ll focus on next, add a little more complexity.

5.1.2 Preference access permissions

You can open and create SharedPreferences with any combination of several Context mode constants. Because these values are int types, you can add them, as in listings 5.1 and 5.2, to combine permissions. The following mode constants are supported:

 Context.MODE_PRIVATE (value 0)

 Context.MODE_WORLD_READABLE (value 1)  Context.MODE_WORLD_WRITEABLE (value 2)

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

Figure 5.1 shows the Android Eclipse plug-in File Explorer view. Within the explorer, you can see the Linux-level permissions for the SharedPreferencesXML files that we created from the SharedPreferences in listing 5.1.

Each Linux 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 other types such as symbolic links have unique types as well). After the type, the three sets of rwx represent the combination of read, write, and execute permissions for user, group, and world, in that order. Looking at this nota- tion, you can tell which files are accessible by the user they’re owned by, by the group they belong to, or by everyone else on the device. Note that the user and group always have full permission to read and write, whereas the final set of permissions fluctuates based on the preference’s mode.

Android puts SharedPreferencesXML files in the /data/data/PACKAGE_NAME/ shared_prefs path on the filesystem. An application or package usually has its own

135

Using preferences

user ID. When an application creates files, including SharedPreferences, they’re owned by that application’s user ID. To allow other applications to access these files, you have to set the world permissions, as shown in figure 5.1.

If you want to access another application’s files, you must know the starting path. The path comes from the Context. To get files from another application, you have to know and use that application’s Context. Android doesn’t officially condone sharing preferences across multiple applications; in practice, apps should use a content pro- vider to share this kind of data. Even so, looking at SharedPreferences does show the underlying data storage models in Android. The following listing shows how to get the SharedPreferences we set in listing 5.1 again, this time from a different application (different .apk and different package).

package com.other.manning.chapter5.prefs; . . . 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);

} catch (NameNotFoundException e) { // log and/or handle

} prefsPrivate = otherAppsContext.getSharedPreferences( SharedPrefTestOtherOutput.PREFS_PRIVATE, 0); prefsWorldRead = otherAppsContext.getSharedPreferences( SharedPrefTestOtherOutput.PREFS_WORLD_READ, 0); prefsWorldWrite = otherAppsContext.getSharedPreferences( SharedPrefTestOtherOutput.PREFS_WORLD_WRITE, 0); prefsWorldReadWrite = otherAppsContext.getSharedPreferences( SharedPrefTestOtherOutput.PREFS_WORLD_READ_WRITE, 0); outputPrivate.setText(

Listing 5.3 Getting SharedPreferences data stored in a different application

Directories with the world x permission

In Android, each package directory is created with the worldx permission. This per- mission means anyone can search and list the files in the directory, which means that Android packages have directory-level access to one another’s files. From there, file-level access determines file permissions.

Use different package

B

Get another application’s context

C

Use otherAppsContext

D

prefsPrivate.getString( SharedPrefTestOtherOutput.KEY_PRIVATE, "NA")); outputWorldRead.setText( prefsWorldRead.getString( SharedPrefTestOtherOutput.KEY_WORLD_READ, "NA")); outputWorldWrite.setText( prefsWorldWrite.getString( SharedPrefTestOtherOutput.KEY_WORLD_WRITE, "NA")); outputWorldReadWrite.setText( prefsWorldReadWrite.getString( SharedPrefTestOtherOutput.KEY_WORLD_READ_WRITE,"NA")); } }

To get one application’s SharedPreferences from another application’s package

B

, we use the createPackageContext(StringcontextName,intmode) method

C

. When we have the other application’s Context, we can use the same names for the Shared- Preferences objects that the other application created to access those preferences

D

. With these examples, we now have one application that sets and gets Shared- Preferences, and a second application with a different .apk file that gets the prefer- ences set by the first. The composite screen shot in figure 5.2 shows what the apps look like. NA indicates a preference we couldn’t access from the second application, either as the result of permissions that were set or because no permissions had been created.

Though SharedPreferences are ultimately backed by XML files on the Android filesystem, you can also directly create, read, and manipulate files, as we’ll discuss in the next section.

Figure 5.2

Two separate applications getting and setting

137

Using the filesystem

5.2

Using the filesystem

Android’s filesystem is based on Linux and supports mode-based permissions. You can access this filesystem in several ways. You can create and read files from within applica- tions, you can access raw resource files, and you can work with specially compiled cus- tom XML files. In this section, we’ll explore each approach.

5.2.1 Creating files

Android’s stream-based system of manipulating files will feel familiar to anyone who’s written I/O code in Java SE or Java ME. You can easily create files in Android and store them in your application’s data path. The following listing demonstrates how to open a FileOutputStream and use it to create a file.

public class CreateFile extends Activity { private EditText createInput;

private Button createButton; @Override

public void onCreate(Bundle icicle) { super.onCreate(icicle); setContentView(R.layout.create_file); createInput = (EditText) findViewById(R.id.create_input); createButton = (Button) findViewById(R.id.create_button); createButton.setOnClickListener(new OnClickListener() { public void onClick(final View v) {

FileOutputStream fos = null; try { fos = openFileOutput("filename.txt", Context.MODE_PRIVATE); fos.write(createInput.getText(). toString().getBytes()); } catch (FileNotFoundException e) { Log.e("CreateFile", e.getLocalizedMessage()); } catch (IOException e) { Log.e("CreateFile", e.getLocalizedMessage()); } finally { if (fos != null) { try { fos.flush(); fos.close(); } catch (IOException e) { // swallow } } } startActivity(

new Intent(CreateFile.this, ReadFile.class)); }

}); } }

Listing 5.4 Creating a file in Android from an Activity

Use openFileOutput

B

Write data to stream

C

Flush and close stream

D

Android provides a convenience method on Context to get a FileOutputStream— namely openFileOutput(Stringname,intmode)

B

. Using this method, you can cre- ate a stream to a file. That file will ultimately be stored at the data/data/ [PACKAGE_NAME]/files/file.name path on the platform. After you have the stream, you can write to it as you would with typical Java

C

. After you’re finished with a stream, you should flush and close it to clean up

D

.

Reading from a file within an application context (within the package path of the application) is also simple; in the next section we’ll show you how.

5.2.2 Accessing files

Similar to openFileOutput(), the Context also has a convenience openFileInput() method. You can use this method to access a file on the filesystem and read it in, as shown in the following listing.

public class ReadFile extends Activity { private TextView readOutput;

private Button gotoReadResource; @Override

public void onCreate(Bundle icicle) { super.onCreate(icicle);

setContentView(R.layout.read_file); readOutput =

(TextView) findViewById(R.id.read_output); FileInputStream fis = null;

try {

fis = openFileInput("filename.txt"); byte[] reader = new byte[fis.available()]; while (fis.read(reader) != -1) {}

readOutput.setText(new String(reader)); } catch (IOException e) {

Log.e("ReadFile", e.getMessage(), e); } finally { if (fis != null) { try { fis.close(); } catch (IOException e) { // swallow } } }

. . . goto next Activity via startActivity omitted for brevity }

}

For input, you use openFileInput(Stringname,intmode) to get the stream

B

, and then read the file into a byte array as with standard Java

C

. Afterward, close the stream properly to avoid hanging on to resources.

With openFileOutput and openFileInput, you can write to and read from any file within the files directory of the application package you’re working in. Also, as we

Listing 5.5 Accessing an existing file in Android from an Activity

Use openFileInput for stream

B

Read data from stream

C

139

Using the filesystem

discussed in the previous section, you can access files across different applications if the permissions allow it and if you know the package used to obtain the full path to the file.

In addition to creating files from within your application, you can push and pull files to the platform using the adb tool, described in section 2.2.3. The File Explorer window in Eclipse provides a UI for moving files on and off the device or simulator. You can optionally put such files in the directory for your application; when they’re there, you can read these files just like you would any other file. Keep in mind that outside of development-related use, you won’t usually push and pull files. Rather, you’ll create and read files from within the application or work with files included with an application as raw resources, as you’ll see next.

5.2.3 Files as raw resources

If you want to include raw files with your application, you can do so using the res/raw resources location. We discussed resources in general in chapter 3. When you place a file in the res/raw location, it’s not compiled by the platform but is available as a raw

resource, as shown in the following listing.

public class ReadRawResourceFile extends Activity { private TextView readOutput;

private Button gotoReadXMLResource; @Override

public void onCreate(Bundle icicle) { super.onCreate(icicle);

setContentView(R.layout.read_rawresource_file); readOutput =

(TextView) findViewById(R.id.readrawres_output); Resources resources = getResources();

InputStream is = null; try {

is = resources.openRawResource(R.raw.people); byte[] reader = new byte[is.available()]; while (is.read(reader) != -1) {}

readOutput.setText(new String(reader)); } catch (IOException e) {

Listing 5.6 Accessing a noncompiled raw file from res/raw Running a bundle of apps with the same user ID

Occasionally, setting the user ID of your application can be extremely useful. For instance, if you have multiple applications that need to share data with one another, but you also don’t want that data to be accessible outside that group of applications, you might want to make the permissions private and share the UID to allow access. You can allow a shared UID by using the sharedUserId attribute in your manifest: android:sharedUserId="YourID". Hold raw resource with InputStream

B

C

Use getResources().openRawResource()

Log.e("ReadRawResourceFile", e.getMessage(), e); } finally { if (is != null) { try { is.close(); } catch (IOException e) { // swallow } } }

. . . go to next Activity via startActivity omitted for brevity }

}

Accessing raw resources closely resembles accessing files. You open a handle to an InputStream

B

. You call Context.getResources() to get the Resources for your cur- rent application’s context and then call openRawResource(intid) to link to the par- ticular item you want

C

. Android will automatically generate the ID within the R class if you place your asset in the res/raw directory. You can use any file as a raw resource, including text, images, documents, or videos. The platform doesn’t precompile raw resources.

The last type of file resource we need to discuss is the res/xml type, which the plat- form compiles into an efficient binary type accessed in a special manner.

5.2.4 XML file resources

The term XML resources sometimes confuses new Android developers. XML resources might mean resources in general that are defined in XML—such as layout files, styles, arrays, and the like—or it can specifically mean res/xml XML files.

In this section, we’ll deal with res/xml XML files. These files are different from raw files in that you don’t use a stream to access them because they’re compiled into an efficient binary form when deployed. They’re different from other resources in that they can be of any custom XML structure.

To demonstrate this concept, we’re going to use an XML file named people.xml that defines multiple <person> elements and uses attributes for firstname and lastname. We’ll grab this resource and display its elements in last-name, first-name order, as shown in figure 5.3.

Our data file for this process, which we’ll place in res/xml, is shown in the following listing.

Figure 5.3 The example

ReadXMLResourceFile Activity that we’ll create in listing 5.8, which reads a res/xml resource file

141

Using the filesystem

<people>

<person firstname="John" lastname="Ford" /> <person firstname="Alfred" lastname="Hitchcock" /> <person firstname="Stanley" lastname="Kubrick" /> <person firstname="Wes" lastname="Anderson" /> </people>

If you’re using Eclipse, it’ll automatically detect a file in the res/xml path and compile it into a resource asset. You can then access this asset in code by parsing its binary XML, as shown in the following listing.

public class ReadXMLResourceFile extends Activity { private TextView readOutput;

@Override

public void onCreate(Bundle icicle) { super.onCreate(icicle); setContentView(R.layout.read_xmlresource_file); readOutput = (TextView) findViewById(R.id.readxmlres_output); XmlPullParser parser = getResources().getXml(R.xml.people); StringBuffer sb = new StringBuffer(); try {

while (parser.next() != XmlPullParser.END_DOCUMENT) { String name = parser.getName();

String first = null; String last = null;

if ((name != null) && name.equals("person")) { int size = parser.getAttributeCount(); for (int i = 0; i < size; i++) { String attrName = parser.getAttributeName(i); String attrValue = parser.getAttributeValue(i); if ((attrName != null) && attrName.equals("firstname")) { first = attrValue;

} else if ((attrName != null) && attrName.equals("lastname")) { last = attrValue;

} }

if ((first != null) && (last != null)) { sb.append(last + ", " + first + "\n"); } } } readOutput.setText(sb.toString()); } catch (Exception e) {

Log.e(“ReadXMLResourceFile”, e.getMessage(), e); }

Listing 5.7 A custom XML file included in res/xml

Listing 5.8 Accessing a compiled XML resource from res/xml

In document CUADERNO PROf Loreto Vargas (página 32-37)

Documento similar