page.title=Content Providers @jd:body

In this document

  1. Content provider basics
  2. Querying a content provider
  3. Modifying data in a provider
  4. Creating a content provider
  5. Content URI summary

Key classes

  1. {@link android.content.ContentProvider}
  2. {@link android.content.ContentResolver}
  3. {@link android.database.Cursor}

Content providers store and retrieve data and make it accessible to all applications. They're the only way to share data across applications; there's no common storage area that all Android packages can access.

Android ships with a number of content providers for common data types (audio, video, images, personal contact information, and so on). You can see some of them listed in the {@link android.provider android.provider} package. You can query these providers for the data they contain (although, for some, you must acquire the proper permission to read the data).

If you want to make your own data public, you have two options: You can create your own content provider (a {@link android.content.ContentProvider} subclass) or you can add the data to an existing provider — if there's one that controls the same type of data and you have permission to write to it.

This document is an introduction to using content providers. After a brief discussion of the fundamentals, it explores how to query a content provider, how to modify data controlled by a provider, and how to create a content provider of your own.

Content Provider Basics

How a content provider actually stores its data under the covers is up to its designer. But all content providers implement a common interface for querying the provider and returning results — as well as for adding, altering, and deleting data.

It's an interface that clients use indirectly, most generally through {@link android.content.ContentResolver} objects. You get a ContentResolver by calling {@link android.content.Context#getContentResolver getContentResolver()} from within the implementation of an Activity or other application component:

ContentResolver cr = getContentResolver();

You can then use the ContentResolver's methods to interact with whatever content providers you're interested in.

When a query is initiated, the Android system identifies the content provider that's the target of the query and makes sure that it is up and running. The system instantiates all ContentProvider objects; you never need to do it on your own. In fact, you never deal directly with ContentProvider objects at all. Typically, there's just a single instance of each type of ContentProvider. But it can communicate with multiple ContentResolver objects in different applications and processes. The interaction between processes is handled by the ContentResolver and ContentProvider classes.

The data model

Content providers expose their data as a simple table on a database model, where each row is a record and each column is data of a particular type and meaning. For example, information about people and their phone numbers might be exposed as follows:

_ID NUMBER NUMBER_KEY LABEL NAME TYPE
13 (425) 555 6677 425 555 6677 Kirkland office Bully Pulpit {@code TYPE_WORK}
44 (212) 555-1234 212 555 1234 NY apartment Alan Vain {@code TYPE_HOME}
45 (212) 555-6657 212 555 6657 Downtown office Alan Vain {@code TYPE_MOBILE}
53 201.555.4433 201 555 4433 Love Nest Rex Cars {@code TYPE_HOME}

Every record includes a numeric {@code _ID} field that uniquely identifies the record within the table. IDs can be used to match records in related tables — for example, to find a person's phone number in one table and pictures of that person in another.

A query returns a {@link android.database.Cursor} object that can move from record to record and column to column to read the contents of each field. It has specialized methods for reading each type of data. So, to read a field, you must know what type of data the field contains. (There's more on query results and Cursor objects later.)

URIs

Each content provider exposes a public URI (wrapped as a {@link android.net.Uri} object) that uniquely identifies its data set. A content provider that controls multiple data sets (multiple tables) exposes a separate URI for each one. All URIs for providers begin with the string "{@code content://}". The {@code content:} scheme identifies the data as being controlled by a content provider.

If you're defining a content provider, it's a good idea to also define a constant for its URI, to simplify client code and make future updates cleaner. Android defines {@code CONTENT_URI} constants for all the providers that come with the platform. For example, the URI for the table that matches phone numbers to people and the URI for the table that holds pictures of people (both controlled by the Contacts content provider) are:

{@code android.provider.Contacts.Phones.CONTENT_URI}
{@code android.provider.Contacts.Photos.CONTENT_URI}

The URI constant is used in all interactions with the content provider. Every {@link android.content.ContentResolver} method takes the URI as its first argument. It's what identifies which provider the ContentResolver should talk to and which table of the provider is being targeted.

Querying a Content Provider

You need three pieces of information to query a content provider:

If you're querying a particular record, you also need the ID for that record.

Making the query

To query a content provider, you can use either the {@link android.content.ContentResolver#query ContentResolver.query()} method or the {@link android.app.Activity#managedQuery Activity.managedQuery()} method. Both methods take the same set of arguments, and both return a Cursor object. However, {@code managedQuery()} causes the activity to manage the life cycle of the Cursor. A managed Cursor handles all of the niceties, such as unloading itself when the activity pauses, and requerying itself when the activity restarts. You can ask an Activity to begin managing an unmanaged Cursor object for you by calling {@link android.app.Activity#startManagingCursor Activity.startManagingCursor()}.

The first argument to either {@link android.content.ContentResolver#query query()} or {@link android.app.Activity#managedQuery managedQuery()} is the provider URI — the {@code CONTENT_URI} constant that identifies a particular ContentProvider and data set (see URIs earlier).

To restrict a query to just one record, you can append the {@code _ID} value for that record to the URI — that is, place a string matching the ID as the last segment of the path part of the URI. For example, if the ID is 23, the URI would be:

{@code content://. . . ./23}

There are some helper methods, particularly {@link android.content.ContentUris#withAppendedId ContentUris.withAppendedId()} and {@link android.net.Uri#withAppendedPath Uri.withAppendedPath()}, that make it easy to append an ID to a URI. Both are static methods that return a Uri object with the ID added. So, for example, if you were looking for record 23 in the database of people contacts, you might construct a query as follows:

import android.provider.Contacts.People;
import android.content.ContentUris;
import android.net.Uri;
import android.database.Cursor;

// Use the ContentUris method to produce the base URI for the contact with _ID == 23.
Uri myPerson = ContentUris.withAppendedId(People.CONTENT_URI, 23);

// Alternatively, use the Uri method to produce the base URI.
// It takes a string rather than an integer.
Uri myPerson = Uri.withAppendedPath(People.CONTENT_URI, "23");

// Then query for this specific record:
Cursor cur = managedQuery(myPerson, null, null, null, null);

The other arguments to the {@link android.content.ContentResolver#query query()} and {@link android.app.Activity#managedQuery managedQuery()} methods delimit the query in more detail. They are:

Let's look at an example query to retrieve a list of contact names and their primary phone numbers:

import android.provider.Contacts.People;
import android.database.Cursor;

// Form an array specifying which columns to return. 
String[] projection = new String[] {
                             People._ID,
                             People._COUNT,
                             People.NAME,
                             People.NUMBER
                          };

// Get the base URI for the People table in the Contacts content provider.
Uri contacts =  People.CONTENT_URI;

// Make the query. 
Cursor managedCursor = managedQuery(contacts,
                         projection, // Which columns to return 
                         null,       // Which rows to return (all rows)
                         null,       // Selection arguments (none)
                         // Put the results in ascending order by name
                         People.NAME + " ASC");

This query retrieves data from the People table of the Contacts content provider. It gets the name, primary phone number, and unique record ID for each contact. It also reports the number of records that are returned as the {@code _COUNT} field of each record.

The constants for the names of the columns are defined in various interfaces — {@code _ID} and {@code _COUNT} in {@link android.provider.BaseColumns BaseColumns}, {@code NAME} in {@link android.provider.Contacts.PeopleColumns PeopleColumns}, and {@code NUMBER} in {@link android.provider.Contacts.PhonesColumns PhoneColumns}. The {@link android.provider.Contacts.People Contacts.People} class implements each of these interfaces, which is why the code example above could refer to them using just the class name.

What a query returns

A query returns a set of zero or more database records. The names of the columns, their default order, and their data types are specific to each content provider. But every provider has an {@code _ID} column, which holds a unique numeric ID for each record. Every provider can also report the number of records returned as the {@code _COUNT} column; its value is the same for all rows.

Here is an example result set for the query in the previous section:

_ID _COUNT NAME NUMBER
44 3 Alan Vain 212 555 1234
13 3 Bully Pulpit 425 555 6677
53 3 Rex Cars 201 555 4433

The retrieved data is exposed by a {@link android.database.Cursor Cursor} object that can be used to iterate backward or forward through the result set. You can use this object only to read the data. To add, modify, or delete data, you must use a ContentResolver object.

Reading retrieved data

The Cursor object returned by a query provides access to a recordset of results. If you have queried for a specific record by ID, this set will contain only one value. Otherwise, it can contain multiple values. (If there are no matches, it can also be empty.) You can read data from specific fields in the record, but you must know the data type of the field, because the Cursor object has a separate method for reading each type of data — such as {@link android.database.Cursor#getString getString()}, {@link android.database.Cursor#getInt getInt()}, and {@link android.database.Cursor#getFloat getFloat()}. (However, for most types, if you call the method for reading strings, the Cursor object will give you the String representation of the data.) The Cursor lets you request the column name from the index of the column, or the index number from the column name.

The following snippet demonstrates reading names and phone numbers from the query illustrated earlier:

import android.provider.Contacts.People;

private void getColumnData(Cursor cur){ 
    if (cur.moveToFirst()) {

        String name; 
        String phoneNumber; 
        int nameColumn = cur.getColumnIndex(People.NAME); 
        int phoneColumn = cur.getColumnIndex(People.NUMBER);
        String imagePath; 
    
        do {
            // Get the field values
            name = cur.getString(nameColumn);
            phoneNumber = cur.getString(phoneColumn);
           
	    // Do something with the values. 
            ... 

        } while (cur.moveToNext());

    }
}

If a query can return binary data, such as an image or sound, the data may be directly entered in the table or the table entry for that data may be a string specifying a {@code content:} URI that you can use to get the data. In general, smaller amounts of data (say, from 20 to 50K or less) are most often directly entered in the table and can be read by calling {@link android.database.Cursor#getBlob Cursor.getBlob()}. It returns a byte array.

If the table entry is a {@code content:} URI, you should never try to open and read the file directly (for one thing, permissions problems can make this fail). Instead, you should call {@link android.content.ContentResolver#openInputStream ContentResolver.openInputStream()} to get an {@link java.io.InputStream} object that you can use to read the data.

Modifying Data

Data kept by a content provider can be modified by:

All data modification is accomplished using {@link android.content.ContentResolver} methods. Some content providers require a more restrictive permission for writing data than they do for reading it. If you don't have permission to write to a content provider, the ContentResolver methods will fail.

Adding records

To add a new record to a content provider, first set up a map of key-value pairs in a {@link android.content.ContentValues} object, where each key matches the name of a column in the content provider and the value is the desired value for the new record in that column. Then call {@link android.content.ContentResolver#insert ContentResolver.insert()} and pass it the URI of the provider and the ContentValues map. This method returns the full URI of the new record — that is, the provider's URI with the appended ID for the new record. You can then use this URI to query and get a Cursor over the new record, and to further modify the record. Here's an example:

import android.provider.Contacts.People;
import android.content.ContentResolver;
import android.content.ContentValues; 

ContentValues values = new ContentValues();

// Add Abraham Lincoln to contacts and make him a favorite.
values.put(People.NAME, "Abraham Lincoln");
// 1 = the new contact is added to favorites
// 0 = the new contact is not added to favorites
values.put(People.STARRED, 1);

Uri uri = getContentResolver().insert(People.CONTENT_URI, values);

Adding new values

Once a record exists, you can add new information to it or modify existing information. For example, the next step in the example above would be to add contact information — like a phone number or an IM or e-mail address — to the new entry.

The best way to add to a record in the Contacts database is to append the name of the table where the new data goes to the URI for the record, then use the amended URI to add the new data values. Each Contacts table exposes a name for this purpose as a {@code CONTENT_DIRECTORY} constant. The following code continues the previous example by adding a phone number and e-mail address for the record just created:

Uri phoneUri = null;
Uri emailUri = null;

// Add a phone number for Abraham Lincoln.  Begin with the URI for
// the new record just returned by insert(); it ends with the _ID
// of the new record, so we don't have to add the ID ourselves.
// Then append the designation for the phone table to this URI,
// and use the resulting URI to insert the phone number.
phoneUri = Uri.withAppendedPath(uri, People.Phones.CONTENT_DIRECTORY);

values.clear();
values.put(People.Phones.TYPE, People.Phones.TYPE_MOBILE);
values.put(People.Phones.NUMBER, "1233214567");
getContentResolver().insert(phoneUri, values);

// Now add an email address in the same way.
emailUri = Uri.withAppendedPath(uri, People.ContactMethods.CONTENT_DIRECTORY);

values.clear();
// ContactMethods.KIND is used to distinguish different kinds of
// contact methods, such as email, IM, etc. 
values.put(People.ContactMethods.KIND, Contacts.KIND_EMAIL);
values.put(People.ContactMethods.DATA, "test@example.com");
values.put(People.ContactMethods.TYPE, People.ContactMethods.TYPE_HOME);
getContentResolver().insert(emailUri, values);   

You can place small amounts of binary data into a table by calling the version of {@link android.content.ContentValues#put ContentValues.put()} that takes a byte array. That would work for a small icon-like image or a short audio clip, for example. However, if you have a large amount of binary data to add, such as a photograph or a complete song, put a {@code content:} URI for the data in the table and call {@link android.content.ContentResolver#openOutputStream ContentResolver.openOutputStream()} with the file's URI. (That causes the content provider to store the data in a file and record the file path in a hidden field of the record.)

In this regard, the {@link android.provider.MediaStore} content provider, the main provider that dispenses image, audio, and video data, employs a special convention: The same URI that is used with {@code query()} or {@code managedQuery()} to get meta-information about the binary data (such as, the caption of a photograph or the date it was taken) is used with {@code openInputStream()} to get the data itself. Similarly, the same URI that is used with {@code insert()} to put meta-information into a MediaStore record is used with {@code openOutputStream()} to place the binary data there. The following code snippet illustrates this convention:

import android.provider.MediaStore.Images.Media;
import android.content.ContentValues;
import java.io.OutputStream;

// Save the name and description of an image in a ContentValues map.  
ContentValues values = new ContentValues(3);
values.put(Media.DISPLAY_NAME, "road_trip_1");
values.put(Media.DESCRIPTION, "Day 1, trip to Los Angeles");
values.put(Media.MIME_TYPE, "image/jpeg");

// Add a new record without the bitmap, but with the values just set.
// insert() returns the URI of the new record.
Uri uri = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, values);

// Now get a handle to the file for that record, and save the data into it.
// Here, sourceBitmap is a Bitmap object representing the file to save to the database.
try {
    OutputStream outStream = getContentResolver().openOutputStream(uri);
    sourceBitmap.compress(Bitmap.CompressFormat.JPEG, 50, outStream);
    outStream.close();
} catch (Exception e) {
    Log.e(TAG, "exception while writing image", e);
}

Batch updating records

To batch update a group of records (for example, to change "NY" to "New York" in all fields), call the {@link android.content.ContentResolver#update ContentResolver.update()} method with the columns and values to change.

Deleting a record

To delete a single record, call {{@link android.content.ContentResolver#delete ContentResolver.delete()} with the URI of a specific row.

To delete multiple rows, call {@link android.content.ContentResolver#delete ContentResolver.delete()} with the URI of the type of record to delete (for example, {@code android.provider.Contacts.People.CONTENT_URI}) and an SQL {@code WHERE} clause defining which rows to delete. (Caution: Be sure to include a valid {@code WHERE} clause if you're deleting a general type, or you risk deleting more records than you intended!).

Creating a Content Provider

To create a content provider, you must:

The following sections have notes on the last two of these tasks.

Extending the ContentProvider class

You define a {@link android.content.ContentProvider} subclass to expose your data to others using the conventions expected by ContentResolver and Cursor objects. Principally, this means implementing six abstract methods declared in the ContentProvider class:

{@code query()}
{@code insert()}
{@code update()}
{@code delete()}
{@code getType()}
{@code onCreate()}

The {@code query()} method must return a {@link android.database.Cursor} object that can iterate over the requested data. Cursor itself is an interface, but Android provides some ready-made Cursor objects that you can use. For example, {@link android.database.sqlite.SQLiteCursor} can iterate over data stored in an SQLite database. You get the Cursor object by calling any of the {@link android.database.sqlite.SQLiteDatabase SQLiteDatabase} class's {@code query()} methods. There are other Cursor implementations — such as {@link android.database.MatrixCursor} — for data not stored in a database.

Because these ContentProvider methods can be called from various ContentResolver objects in different processes and threads, they must be implemented in a thread-safe manner.

As a courtesy, you might also want to call {@link android.content.ContentResolver#notifyChange(android.net.Uri,android.database.ContentObserver) ContentResolver.notifyChange()} to notify listeners when there are modifications to the data.

Beyond defining the subclass itself, there are other steps you should take to simplify the work of clients and make the class more accessible:

For an example of a private content provider implementation, see the NodePadProvider class in the Notepad sample application that ships with the SDK.

Declaring the content provider

To let the Android system know about the content provider you've developed, declare it with a {@code <provider>} element in the application's AndroidManifest.xml file. Content providers that are not declared in the manifest are not visible to the Android system

The {@code name} attribute is the fully qualified name of the ContentProvider subclass. The {@code authorities} attribute is the authority part of the {@code content:} URI that identifies the provider. For example if the ContentProvider subclass is AutoInfoProvider, the {@code <provider>} element might look like this:

<provider android:name="com.example.autos.AutoInfoProvider"
          android:authorities="com.example.autos.autoinfoprovider" 
          . . . />
</provider>

Note that the {@code authorities} attribute omits the path part of a {@code content:} URI. For example, if AutoInfoProvider controlled subtables for different types of autos or different manufacturers,

{@code content://com.example.autos.autoinfoprovider/honda}
{@code content://com.example.autos.autoinfoprovider/gm/compact}
{@code content://com.example.autos.autoinfoprovider/gm/suv}

those paths would not be declared in the manifest. The authority is what identifies the provider, not the path; your provider can interpret the path part of the URI in any way you choose.

Other {@code <provider>} attributes can set permissions to read and write data, provide for an icon and text that can be displayed to users, enable and disable the provider, and so on. Set the {@code multiprocess} attribute to "{@code true}" if data does not need to be synchronized between multiple running versions of the content provider. This permits an instance of the provider to be created in each client process, eliminating the need to perform IPC.

Content URI Summary

Here is a recap of the important parts of a content URI:

Elements of a content URI

  1. Standard prefix indicating that the data is controlled by a content provider. It's never modified.
  2. The authority part of the URI; it identifies the content provider. For third-party applications, this should be a fully-qualified class name (reduced to lowercase) to ensure uniqueness. The authority is declared in the {@code <provider>} element's {@code authorities} attribute:

    <provider android:name=".TransportationProvider"
              android:authorities="com.example.transportationprovider"
              . . .  >
  3. The path that the content provider uses to determine what kind of data is being requested. This can be zero or more segments long. If the content provider exposes only one type of data (only trains, for example), it can be absent. If the provider exposes several types, including subtypes, it can be several segments long — for example, "{@code land/bus}", "{@code land/train}", "{@code sea/ship}", and "{@code sea/submarine}" to give four possibilities.

  4. The ID of the specific record being requested, if any. This is the {@code _ID} value of the requested record. If the request is not limited to a single record, this segment and the trailing slash are omitted:

    {@code content://com.example.transportationprovider/trains}