Chapter 6. Data Persistence

WHAT YOU WILL LEARN IN THIS CHAPTER

  • How to save simple data using the SharedPreferences object

  • How to write and read files in internal and external storage

  • How to create and use a SQLite database

In this chapter, you will learn how to persist data in your Android applications. Persisting data is an important topic in application development, as users expect to reuse the data sometime at a later stage. For Android, there are primarily three basic ways of persisting data:

A lightweight mechanism known as shared preferences to save small chunks of data

  • Traditional file systems

  • A relational database management system through the support of SQLite databases

The techniques discussed in this chapter enable applications to create and access their own private data. In the next chapter you'll learn how you can share data across applications.

SAVING AND LOADING USER PREFERENCES

Android provides the SharedPreferences object to help you save simple application data. For example, your application may have an option to allow users to specify the font size of the text displayed in your application. In this case, your application needs to remember the size set by the user so that the next time he or she uses the application again, your application can set the size appropriately. In order to do so, you have several options. You can save the data to a file, but you have to perform some file management routines, such as writing the data to the file, indicating how many characters to read from it, and so on. Also, if you have several pieces of information to save, such as text size, font name, preferred background color, and so on, then the task of writing to a file becomes more onerous.

An alternative to writing to a text file is to use a database, but saving simple data to a database is overkill, both from a developer's point of view and in terms of the application's run-time performance.

Using the SharedPreferences object, however, you save the data you want through the use of key/value pairs — specify a key for the data you want to save, and then both it and its value will be saved automatically to an XML file for you.

Using getSharedPreferences()

To see how the SharedPreferences object works, the following Try It Out demonstrates how easy it is to save user data to an XML file, and then retrieve it easily via the same object.

Using getPreferences()

In the previous section, you used the used the SharedPreferences object by supplying it with a name, like this:

//---get the SharedPreferences object---
prefs = getSharedPreferences(prefName, MODE_PRIVATE);

In this case, the information saved inside the SharedPreferences object is visible to all the activities within the same application. However, if you don't need to share the data between activities, you can use the getPreferences() method, like this:

//---get the SharedPreferences object---
prefs = getPreferences(MODE_PRIVATE);

The getPreferences() method does not require a name, and the data saved is restricted to the activity that created it. In this case, the filename used for the preferences file will be named after the activity that created it (see Figure 6-4).

FIGURE 6-4

Figure 6-4. FIGURE 6-4

PERSISTING DATA TO FILES

The SharedPreferences object allows you to store data that could best be stored as key/value pairs, for example, data such as user ID, birthdate, gender, driving license number, etc. However, sometimes you might prefer to use the traditional file system to store your data. For example, you might want to store text of poems that you want to display in your applications. In Android, you can use the classes in the java.io namespace to do so.

Saving to Internal Storage

The first way to save files in your Android application is to write to the device's internal storage. The following Try It Out demonstrates how to save a string entered by the user to the device's internal storage.

Saving to External Storage (SD Card)

The previous section showed how you can save your files to the internal storage of your Android device. Sometimes, it would be useful to save them to external storage (such as an SD card) because of its larger capacity, as well as the capability to share the files easily with other users (by removing the SD card and passing it to somebody else).

Using the project created in the previous section as the example, to save the text entered by the user in the SD card, modify the onClick() method of the Save button as shown in bold here:

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

        String str = textBox.getText().toString();
        try
        {
            //---SD Card Storage---
            File sdCard = Environment.getExternalStorageDirectory();
            File directory = new File (sdCard.getAbsolutePath() +
                "/MyFiles");
            directory.mkdirs();
            File file = new File(directory, "textfile.txt");
            FileOutputStream fOut = new FileOutputStream(file);

            OutputStreamWriter osw = new
            OutputStreamWriter(fOut);

            //---write the string to the file---
            osw.write(str);
osw.flush();
            osw.close();

            //---display file saved message---
            Toast.makeText(getBaseContext(),
                "File saved successfully!",
                Toast.LENGTH_SHORT).show();

            //---clears the EditText---
            textBox.setText("");
         }
         catch (IOException ioe)
         {
             ioe.printStackTrace();
         }
    }
});

The preceding code uses the getExternalStorageDirectory() method to return the full path to the external storage. Typically, it should return the "/sdcard" path for a real device, and "/mnt/sdcard" for an Android Emulator. However, you should never try to hardcode the path to the SD card, as manufacturers may choose to assign a different path name to the SD card. Hence, be sure to use the getExternalStorageDirectory() method to return the full path to the SD card.

You then create a directory called MyFiles in the SD card. Finally, you save the file into this directory.

To load the file from the external storage, modify the onClick() method for the Load button:

loadBtn.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
       try
       {
           //---SD Storage---
           File sdCard = Environment.getExternalStorageDirectory();
           File directory = new File (sdCard.getAbsolutePath() +
               "/MyFiles");
           File file = new File(directory, "textfile.txt");
           FileInputStream fIn = new FileInputStream(file);
           InputStreamReader isr = new InputStreamReader(fIn);

           char[] inputBuffer = new char[READ_BLOCK_SIZE];
           String s = "";

           int charRead;
           while ((charRead = isr.read(inputBuffer))>0)
           {
               //---convert the chars to a String---
               String readString =
                   String.copyValueOf(inputBuffer, 0, charRead);
               s += readString;

              inputBuffer = new char[READ_BLOCK_SIZE];
           }
           //---set the EditText to the text that has been
           // read---
textBox.setText(s);

            Toast.makeText(getBaseContext(),
                "File loaded successfully!",
                Toast.LENGTH_SHORT).show();
        }
        catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }
});

Note that in order to write to the external storage, you need to add the WRITE_EXTERNAL_STORAGE permission in your AndroidManifest.xml file:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="net.learn2develop.Files"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".MainActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    <uses-sdk android:minSdkVersion="9" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE">
    </uses-permission>
</manifest>

Choosing the Best Storage Option

And so, you have seen the few ways of saving data in your Android applications — SharedPreferences, internal storage, and external storage. Which one should you use in your applications? Here are some suggestions:

  • If you have data that can be represented using key/value pairs, then use the SharedPreferences object. For example, if you want to store user preference data such as user name, background color, date of birth, last login date, then the SharedPreferences object is the ideal way to store these data. Moreover, you don't really have to get your hands dirty on how these data are stored; all you need is to use the SharedPreferences object to store and retrieve them.

  • If you need to store ad-hoc data, then using the internal storage is a good option. For example, your application (such as an RSS reader) may need to download images from the Web for display. In this scenario, saving the images to internal storage is a good solution. You may also need to persist data created by the user, such as when you have a note-taking application where users can take notes and save them for later use. In all these scenarios, using the internal storage is a good choice.

  • There are times when you need to share your application data with other users. For example, you may create an Android application that logs the coordinates of the locations that a user has been to, and you want to share all these data with other users. In this scenario, you can store your files on the SD card of the device so that users can easily transfer the data to other devices (and computers) for use later.

Using Static Resources

Besides creating and using files dynamically during run time, it is also possible to add files to your package during design time so that you can use it during run time. For example, you may want to bundle some help files with your package so that you can display some help messages when users need it. In this case, you can add the files to the res/raw folder (you need to create this folder yourself) of your package. Figure 6-8 shows the res/raw folder containing a file named textfile.txt.

FIGURE 6-8

Figure 6-8. FIGURE 6-8

To make use of the file in code, use the getResources() method to return a Resources object and then use its openRawResource() method to open the file contained in the res/raw folder:

import java.io.InputStream;
import java.io.BufferedReader;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        InputStream is = this.getResources().openRawResource(R.raw.textfile);
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        String str = null;
        try {
            while ((str = br.readLine()) != null) {
                Toast.makeText(getBaseContext(),
                    str, Toast.LENGTH_SHORT).show();
            }
            is.close();
            br.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        textBox = (EditText) findViewById(R.id.txtText1);
        Button saveBtn = (Button) findViewById(R.id.btnSave);
        Button loadBtn = (Button) findViewById(R.id.btnLoad);

        saveBtn.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
            }
        });

        loadBtn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
       }
   });
}

The resource ID of the resource stored in the res/raw folder is named after its filename without its extension. For example, if the text file is textfile.txt, then its resource ID is R.raw.textfile.

CREATING AND USING DATABASES

So far, all the techniques you have seen are useful for saving simple sets of data. For saving relational data, using a database is much more efficient. For example, if you want to store the results of all the students in a school, it is much more efficient to use a database to represent them because you can use database querying to retrieve the results of the specific students. Moreover, using databases enables you to enforce data integrity by specifying the relationships between different sets of data.

Android uses the SQLite database system. The database that you create for an application is only accessible to itself; other applications will not be able to access it.

In this section, you will learn how to programmatically create a SQLite database in your Android application. For Android, the SQLite database that you create programmatically in an application is always stored in the /data/data/<package_name>/databases folder.

Creating the DBAdapter Helper Class

A good practice for dealing with databases is to create a helper class to encapsulate all the complexities of accessing the data so that it is transparent to the calling code. Hence, for this section, you will create a helper class called DBAdapter that creates, opens, closes, and uses a SQLite database.

FIGURE 6-9

Figure 6-9. FIGURE 6-9

In this example, you are going to create a database named MyDB containing one table named contacts. This table will have three columns: _id, name, and email (see Figure 6-9).

Using the Database Programmatically

You are now ready to use the database using the helper class created in the previous section.

Adding Contacts

The following Try It Out demonstrates how you can add a contact to the table.

Retrieving All the Contacts

To retrieve all the contacts in the contacts table, use the getAllContacts() method of the DBAdapter class, as the following Try It Out shows.

Retrieving a Single Contact

To retrieve a single contact using its ID, call the getContact() method of the DBAdapter class, as the following Try It Out shows.

Updating a Contact

To update a particular contact, call the updateContact() method in the DBAdapter class by passing the ID of the contact you want to update, as the following Try It Out shows.

Deleting a Contact

To delete a contact, use the deleteContact() method in the DBAdapter class by passing the ID of the contact you want to update, as the following Try It Out shows.

Upgrading the Database

Sometimes, after creating and using the database, you may need to add additional tables, change the schema of the database, or add columns to your tables. In this case, you need to migrate your existing data from the old database to a newer one.

To upgrade the database, change the DATABASE_VERSION constant to a value higher than the previous one. For example, if its previous value was 1, change it to 2:

public class DBAdapter {
    public static final String KEY_ROWID = "_id";
    public static final String KEY_NAME = "name";
    public static final String KEY_EMAIL = "email";
    private static final String TAG = "DBAdapter";

    private static final String DATABASE_NAME = "MyDB";
    private static final String DATABASE_TABLE = "contacts";
    private static final int DATABASE_VERSION = 2;

When you run the application one more time, you will see the following message in the LogCat window of Eclipse:

DBAdapter(24096): Upgrading database from version 1 to 2, which will destroy all
old data

In this example, for simplicity you simply drop the existing table and create a new one. In real-life, you usually back up your existing table and then copy them over to the new table.

Pre-Creating the Database

In real-life applications, sometimes it would be more efficient to pre-create the database at design time rather than run time. To pre-create a SQLite database, you can use many of the free tools available on the Internet. One such tool is the SQLite Database Browser, which is available free for the different platforms (http://sourceforge.net/projects/sqlitebrowser/).

Once you have installed the SQLite Database Browser, you can create a database visually. Figure 6-13 shows that I have created a contacts table with the fields indicated.

FIGURE 6-13

Figure 6-13. FIGURE 6-13

Populating the table with rows is also straightforward. Figure 6-14 shows how you can fill the table with data using the Browse Data tab.

FIGURE 6-14

Figure 6-14. FIGURE 6-14

Bundling the Database with an Application

With the database created at design time, the next thing you should do is bundle it together with your application so that you can use it in your application. The following Try It Out shows you how.

SUMMARY

In this chapter, you learned the different ways to save persistent data to your Android device. For simple unstructured data, using the SharedPreferences object is the ideal solution. If you need to store bulk data, then consider using the traditional file system. Finally, for structured data, it is more efficient to store it in a relational database management system. For this, Android provides the SQLite database, which you can access easily using the APIs exposed.

Note that for the SharedPreferences object and the SQLite database, the data is accessible only by the application that creates it. In other words, it is not shareable. If you need to share data among different applications, you need to create a content provider. Content providers are discussed in more detail in the next chapter.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.59.159.113