Chapter 8
Content Providers

The previous chapter explains the various ways to persist data—using shared preferences, files, as well as SQLite databases. Although using the database approach is the recommended way to save structured and complex data, sharing data is a challenge because the database is accessible to only the package that created it.

This chapter explains Android's way of sharing data through the use of content providers. You find out how to use the built-in content providers, as well as implement your own content providers to share data across packages.

SHARING DATA IN ANDROID

In Android, using a content provider is the recommended way to share data across packages. Think of a content provider as a data store. How it stores its data is not relevant to the application using it. However, the way in which packages can access the data stored in it using a consistent programming interface is important. A content provider behaves very much like a database—you can query it, edit its content, and add or delete content. However, unlike a database, a content provider can use different ways to store its data. The data can be stored in a database, in files, or even over a network.

Android ships with many useful content providers, including the following:

  • Browser—Stores data such as browser bookmarks, browser history, and so on
  • CallLog—Stores data such as missed calls, call details, and so on
  • Contacts—Stores contact details
  • MediaStore—Stores media files such as audio, video, and images
  • Settings—Stores the device's settings and preferences

Besides the many built-in content providers, you can also create your own content providers.

To query a content provider, you specify the query string in the form of a Uniform Resource Identifier (URI), with an optional specifier for a particular row. Here's the format of the query URI:

<standard_prefix>://<authority>/<data_path>/<id>

The various parts of the URI are as follows:

  • The standard prefix for content providers is always content://.

    The authority specifies the name of the content provider. An example would be contacts for the built-in Contacts content provider. For third-party content providers, this could be the fully qualified name, such as com.wrox.provider or com.jfdimarzio.provider.

  • The data path specifies the kind of data requested. For example, if you are getting all the contacts from the Contacts content provider then the data path would be people, and the URI would look like this: content://contacts/people.
  • The id specifies the specific record requested. For example, if you are looking for contact number 2 in the Contacts content provider, the URI would look like this: content://contacts/people/2.

Table 8.1 shows some examples of query strings.

Table 8.1 Example Query Strings

QUERY STRING DESCRIPTION
content://media/internal/images Returns a list of the internal images on the device
content://media/external/images Returns a list of the images stored on the external ­storage (for example, SD card) on the device
content://call_log/calls Returns a list of calls registered in the Call Log
content://browser/bookmarks Returns a list of bookmarks stored in the browser

USING A CONTENT PROVIDER

The best way to understand content providers is to actually use one. The following Try It Out shows how you can use a content provider from within your Android application.

Predefined Query String Constants

Besides using the query URI, you can use a list of predefined query string constants in Android to specify the URI for the different data types. For example, besides using the query content://contacts/people, you can rewrite this statement:

        Uri allContacts = Uri.parse("content://contacts/people");

using one of the predefined constants in Android, as follows:

        Uri allContacts = ContactsContract.Contacts.CONTENT_URI;

The PrintContacts() method prints the following in the logcat window:

12-13 08:32:50.471: V/Content Providers(12346): 1, Wei-Meng Lee
12-13 08:32:50.471: V/Content Providers(12346): 2, Linda Chen
12-13 08:32:50.471: V/Content Providers(12346): 3, Joanna Yip

It prints the ID and name of each contact stored in the Contacts application. In this case, you access the ContactsContract.Contacts._ID field to obtain the ID of a contact, and ContactsContract.Contacts.DISPLAY_NAME for the name of a contact. If you want to display the phone number of a contact, you need to query the content provider again, as the information is stored in another table:

    private void PrintContacts(Cursor c)
    {
        if (c.moveToFirst()) {
            do{
                String contactID = c.getString(c.getColumnIndex(
                        ContactsContract.Contacts._ID));
                String contactDisplayName =
                        c.getString(c.getColumnIndex(
                                ContactsContract.Contacts.DISPLAY_NAME));
                Log.v("Content Providers", contactID + ", " +
                        contactDisplayName);
                //---get phone number---
                    Cursor phoneCursor =
                        getContentResolver().query(
                            ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
                            ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " +
                            contactID, null, null);
                    while (phoneCursor.moveToNext()) {
                        Log.v("Content Providers",
                            phoneCursor.getString(
                                phoneCursor.getColumnIndex(
                                    ContactsContract.CommonDataKinds.Phone.NUMBER)));
                    }
                    phoneCursor.close();
            } while (c.moveToNext());
        }
    }

In the preceding code snippet, you first check whether a contact has a phone number using the ContactsContract.Contacts.HAS_PHONE_NUMBER field. If the contact has at least a phone number, you then query the content provider again based on the ID of the contact. After the phone numbers are retrieved, you then iterate through them and print out the numbers. You should see something like this:

12-13 08:59:31.881: V/Content Providers(13351): 1, Wei-Meng Lee
12-13 08:59:32.311: V/Content Providers(13351): +651234567
12-13 08:59:32.321: V/Content Providers(13351): 2, Linda Chen
12-13 08:59:32.511: V/Content Providers(13351): +1 876-543-21
12-13 08:59:32.545: V/Content Providers(13351): 3, Joanna Yip
12-13 08:59:32.641: V/Content Providers(13351): +239 846 5522

Projections

The third parameter for the CursorLoader class controls how many columns are returned by the query. This parameter is known as the projection. Earlier, you specified null:

        Cursor c;
            CursorLoader cursorLoader = new CursorLoader(
                    this,
                    allContacts,
                    null,
                    null,
                    null ,
                    null);
            c = cursorLoader.loadInBackground();            

You can specify the exact columns to return by creating an array containing the name of the column to return, like this:

        String[] projection = new String[]
                {ContactsContract.Contacts._ID,
                 ContactsContract.Contacts.DISPLAY_NAME,
                 ContactsContract.Contacts.HAS_PHONE_NUMBER};
        Cursor c;
            CursorLoader cursorLoader = new CursorLoader(
                    this,
                    allContacts,
                    projection,
                    null,
                    null ,
                    null);
            c = cursorLoader.loadInBackground();            

In the preceding example, the _ID, DISPLAY_NAME, and HAS_PHONE_NUMBER fields are retrieved.

Filtering

The fourth and fifth parameters for the CursorLoader class enable you to specify a SQL WHERE clause to filter the result of the query. For example, the following statement retrieves only the people whose name ends with “Lee”:

        Cursor c;
            CursorLoader cursorLoader = new CursorLoader(
                    this,
                    allContacts,
                    projection,
                    ContactsContract.Contacts.DISPLAY_NAME + " LIKE '%Lee'",
                    null ,
                    null);
            c = cursorLoader.loadInBackground();            

Here, the fourth parameter for the CursorLoader constructor contains a SQL statement containing the name to search for (“Lee”). You can also put the search string into the next argument of the method/constructor, like this:

        Cursor c;
            //---Honeycomb and later---
            CursorLoader cursorLoader = new CursorLoader(
                    this,
                    allContacts,
                    projection,
                    ContactsContract.Contacts.DISPLAY_NAME + " LIKE ?",
                    new String[] {"%Lee"},
                    null);
            c = cursorLoader.loadInBackground();            

Sorting

The last parameter of the CursorLoader class enables you to specify a SQL ORDER BY clause to sort the result of the query. For example, the following statement sorts the contact names in ascending order:

        Cursor c;
            CursorLoader cursorLoader = new CursorLoader(
                    this,
                    allContacts,
                    projection,
                    ContactsContract.Contacts.DISPLAY_NAME + " LIKE ?",
                    new String[] {"%Lee"},
                    ContactsContract.Contacts.DISPLAY_NAME + " ASC");
            c = cursorLoader.loadInBackground();            

CREATING YOUR OWN CONTENT PROVIDERS

Creating your own content provider in Android is relatively simple. All you need to do is extend the abstract ContentProvider class and override the various methods defined within it.

This section explains how to create a simple content provider that stores a list of books.

USING THE CONTENT PROVIDER

Now that you have built your new content provider, you can test it from within your Android application. The following Try It Out demonstrates how to do that.

SUMMARY

In this chapter, you learned what content providers are and how to use some of the built-in content providers in Android. In particular, you have seen how to use the Contacts content provider. Google's decision to provide content providers enables applications to share data through a standard set of programming interfaces. In addition to the built-in content providers, you can also create your own custom content provider to share data with other packages.

EXERCISES

  1. Write the query to retrieve all contacts from the Contacts application that contain the word “jack.”
  2. Name the methods that you need to override in your own implementation of a content provider.
  3. How do you register a content provider in your AndroidManifest.xml file?

    You can find answers to the exercises in the appendix.

WHAT YOU LEARNED IN THIS CHAPTER

TOPIC KEY CONCEPTS
Retrieving a managed cursor Use the CursorLoader class.
Two ways to specify a query for a content provider Use either a query URI or a predefined query string constant.
Retrieving the value of a ­column in a content provider Use the getColumnIndex() method.
Querying URI for accessing a contact's name ContactsContract.Contacts.CONTENT_URI
Querying URI for accessing a contact's phone number ContactsContract.CommonDataKinds.Phone.CONTENT_URI
Creating your own content provider Create a class and extend the ContentProvider class.