blob: 343ed8472f15f6e0c5c6e5ae536ebd52d49a2ba6 [file] [log] [blame]
page.title=Accessing Content Providers
@jd:body
<p>If you want to make your data public, you can create (or call) a content
provider. This is an object that can store and retrieve data accessible by all
applications. This is the only way to share data across packages; there is no
common storage area that all packages can share. 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
Android's native content providers in the {@link android.provider provider} package.</p>
<p>How a content provider actually stores its data under the covers is up to
the implementation of the content provider, but all content providers must
implement a common convention to query for data, and a common convention to
return results. However, a content provider can implement custom helper
functions to make data storage/retrieval simpler for the specific data that it
exposes.</p>
<p>This document covers two topics related to Content Providers:</p>
<ul>
<li><a href="#usingacp">Using a Content Provider</a></li>
<li><a href="#creatingacontentprovider">Creating a Content Provider</a></li>
</ul>
<h2>Using a Content Provider to Store and Retrieve Data <a name="usingacp"
id="usingacp"></a></h2>
<p>This section describes how to store and retrieve data using a content
provider implemented by you or anyone else. Android exposes a number of
content providers for a wide range of data types, from music and image files
to phone numbers. You can see a list of content providers exposed through the
convenience classes in the {@link android.provider android.provider} package.</p>
<p>Android's content providers are loosely linked to their clients. Each
content provider exposes a unique string (a URI) identifying the type of data
that it will handle, and the client must use that string to store or retrieve
data of that type. We'll explain this more in <a href="#querying">Querying for
Data</a>.</p>
<p>This section describes the following activities:</p>
<ul>
<li>
<a href="#querying">Querying for Data</a>
<ul>
<li>Making the query</li>
<li>What the query returns</li>
<li>Querying for files</li>
<li>Reading retrieved data</li>
</ul>
</li>
<li><a href="#modifyingdata">Modifying Data</a></li>
<li><a href="#addingrecord">Adding a Record</a></li>
<li><a href="#deletingrecord">Deleting a Record</a></li>
</ul>
<a name="querying" id="querying"></a>
<h4>Querying for Data</h4>
<p>Each contact provider exposes a unique public URI (wrapped by {@link android.net.Uri})
that is used by a client to query/add/update/delete data on that content
provider. This URI has two forms: one to indicate all values of that type
(e.g., all personal contacts), and one form to indicate a specific record of
that type (e.g., Joe Smith's contact information).</p>
<ul>
<li><strong>content://contacts/people/</strong> is the URI that would return a list of all contact names on the device.</li>
<li><strong>content://contacts/people/23</strong> is the URI string that would return a single result row, the contact with ID = 23. .</li>
</ul>
<p>An application sends a query to the device that specifies a general type of
item (all phone numbers), or a specific item (Bob's phone number), to
retrieve. Android then returns a Cursor over a recordset of results, with a
specific set of columns. Let's look at a hypothetical query string and a
result set (the results have been trimmed a bit for clarity):</p>
<p>query = <code>content://contacts/people/</code></p>
<p>Results:</p>
<table border="1">
<tbody>
<tr>
<th scope="col">_ID</th>
<th scope="col">_COUNT</th>
<th scope="col">NUMBER</th>
<th scope="col">NUMBER_KEY</th>
<th scope="col">LABEL</th>
<th scope="col">NAME</th>
<th scope="col">TYPE</th>
</tr>
<tr>
<td>13</td>
<td>4</td>
<td>(425) 555 6677</td>
<td>425 555 6677</td>
<td>Kirkland office</td>
<td>Bully Pulpit</td>
<td>Work</td>
</tr>
<tr>
<td>44</td>
<td>4</td>
<td>(212) 555-1234</td>
<td>212 555 1234</td>
<td>NY apartment</td>
<td>Alan Vain</td>
<td>Home</td>
</tr>
<tr>
<td>45</td>
<td>4</td>
<td>(212) 555-6657</td>
<td>212 555 6657</td>
<td>Downtown office</td>
<td>Alan Vain</td>
<td>Work</td>
</tr>
<tr>
<td>53</td>
<td>4</td>
<td>201.555.4433</td>
<td>201 555 4433</td>
<td>Love Nest</td>
<td>Rex Cars</td>
<td>Home</td>
</tr>
</tbody>
</table>
<p>Note that the query string isn't a standard SQL query string, but instead a
URI string that describes the type of data to return. This URI consists of
three parts: the string "content://"; a segment that describes what kind of
data to retrieve; and finally an optional ID of a specific item of the
specified content type. Here are a few more example query strings:</p>
<ul>
<li><strong>content://media/internal/images</strong> is the URI string that would return a list of all
the internal images on the device.</li>
<li><strong>content://media/external/images</strong> is the URI string that would return a list of all
the images on the "primary" external storage (e.g., the SD card).</li>
<li><strong>content://contacts/people/</strong> is the URI that would return a list of all contact names on the device.</li>
<li><strong>content://contacts/people/23</strong> is the URI string that would return a single result row, the contact with ID = 23.</li>
</ul>
<p>Although there is a general form, these query URIs are somewhat arbitrary and
confusing. Therefore, Android provides a list of helper classes in the {@link
android.provider} package that
define these query strings so you should not need to know the actual URI value
for different data types. These helper classes define a string (actually,
a {@link android.net.Uri Uri} object)
called CONTENT_URI for a specific data type.</p>
<p>Typically you will use the defined CONTENT_URI object to make a query,
instead of writing the full URI yourself.
So, each of the example query strings listed above (except for the last one that specifies the record ID)
can be acquired with the following Uri references:</p>
<ul>
<li>{@link android.provider.MediaStore.Images.Media#INTERNAL_CONTENT_URI
MediaStore.Images.Media.INTERNAL_CONTENT_URI}</li>
<li>{@link android.provider.MediaStore.Images.Media#EXTERNAL_CONTENT_URI
MediaStore.Images.Media.EXTERNAL_CONTENT_URI}</li>
<li>{@link android.provider.Contacts.People#CONTENT_URI
Contacts.People.CONTENT_URI}</li>
</ul>
<p>To query a specific record ID (e.g., content://contacts/people/23),
you'll use the same CONTENT_URI, but must append the specific ID value that you want.
This is one of the few times you should need to examine or modify the URI string.
So, for example, if you were looking for record 23 in the people contacts, you might run a query
as shown here:</p>
<pre>
// Get the base URI for contact with _ID=23.
// This is same as Uri.parse("content://contacts/people/23");
Uri myPerson = ContentUris.withAppendedId(People.CONTENT_URI, 23);
// Query for this record.
Cursor cur = managedQuery(myPerson, null, null, null);
</pre>
<p class="note"><strong>Tip:</strong> You can also append a string to a Uri, using
{@link android.net.Uri#withAppendedPath(Uri,String)}.</p>
<p>This query returns a cursor over a database query result set. What columns
are returned, what they're called, and what they are named are discussed next.
For now, though, know that you can specify that only certain columns be
returned, the sort order, and a SQL WHERE clause.</p>
<p>You should use the {@link
android.app.Activity#managedQuery(android.net.Uri,java.lang.String[],java.lang.String,java.lang.String[],java.lang.String) Activity.managedQuery()}
method to retrieve a managed cursor. A managed cursor handles all the niceties
such as unloading itself when the application pauses, and requerying itself
when the application restarts. You can ask Android to manage an unmanaged
cursor for you by calling {@link
android.app.Activity#startManagingCursor(android.database.Cursor) Activity.startManagingCursor()}.</p>
<p>Let's look at an example query to retrieve a list of contact names and their primary phone
numbers.</p>
<pre>
// An array specifying which columns to return.
string[] projection = new string[] {
People._ID,
People.NAME,
People.NUMBER,
};
// Get the base URI for People table in Contacts content provider.
// ie. content://contacts/people/
Uri mContacts = People.CONTENT_URI;
// Best way to retrieve a query; returns a managed query.
Cursor managedCursor = managedQuery( mContacts,
projection, //Which columns to return.
null, // WHERE clause--we won't specify.
People.NAME + " ASC"); // Order-by clause.
</pre>
<p>This query will retrieve data from the people table of
the Contacts content provider. It will retrieve the name, primary phone number, and unique record ID for
each contact. </p>
<p><strong>What the query returns</strong></p>
<p>A query returns a set of zero or more database records. The column names,
order, and type are specific to the content provider, but every query includes
a column called _id, which is the ID of the item in that row. If a query can
return binary data, such as a bitmap or audio file, it will have a column with
any name that holds a content:// URI that you can use to get this data (more
information on how to get the file will be given later). Here is a tiresome
example result set for the previous query:</p>
<table border="1">
<tbody>
<tr>
<th scope="col">_id</th>
<th scope="col">name</th>
<th scope="col">number</th>
</tr>
<tr>
<td>44</td>
<td>Alan Vain</td>
<td>212 555 1234</td>
</tr>
<tr>
<td>13</td>
<td>Bully Pulpit</td>
<td>425 555 6677</td>
</tr>
<tr>
<td>53</td>
<td>Rex Cars</td>
<td>201 555 4433</td>
</tr>
</tbody>
</table>
<p>This result set demonstrates what is returned when we specified a subset of
columns to return. The optional subset list is passed in the
<em>projection</em> parameter of the query. A content manager should list
which columns it supports either by implementing a set of interfaces
describing each column (see {@link
android.provider.Contacts.People.Phones Contacts.People.Phones},
which extends {@link android.provider.BaseColumns BaseColumns}, {@link
android.provider.Contacts.PhonesColumns PhonesColumns}, and {@link
android.provider.Contacts.PeopleColumns PeopleColumns}), or by
listing the column names as constants. Note that you need to
know the data type of a column exposed by a content provider in order to be
able to read it; the field reading method is specific to the data type, and a
column's data type is not exposed programmatically.</p>
<p>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 cursor to
read, modify, or delete rows. Adding new rows requires a different object
described later.</p>
<p>Note that by convention, every recordset includes a field named _id, which
is the ID of a specific record, and a _count field, which is a count of
records in the current result set. These field names are defined by {@link
android.provider.BaseColumns BaseColumns}.</p>
<p><strong>Querying for Files</strong></p>
<p>The previous query result demonstrates how a file is returned in a data
set. The file field is typically (but not required to be) a string path to the
file. However, the caller should never try to read and open the file directly
(permissions problems for one thing can make this fail). Instead, you should
call {@link
android.content.ContentResolver#openInputStream(android.net.Uri) ContentResolver.openInputStream()}
/ {@link
android.content.ContentResolver#openOutputStream(android.net.Uri)
ContentResolver.openOutputStream()},
or one of the helper functions from a content provider.</p>
<p><strong>Reading Retrieved Data</strong></p>
<p>The Cursor object retrieved by the 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. You can
read data from specific fields in the record, but you must know the data type
of the field, because reading data requires a specialized method for each type
of data. (If you call the string reading method on most types of columns,
Android will give you the String representation of the data.) The Cursor lets
you request the column name from the index, or the index number from the
column name.</p>
<p>If you are reading binary data, such as an image file, you should call
{@link
android.content.ContentResolver#openOutputStream(android.net.Uri) ContentResolver.openOutputStream()}
on the string content:// URI stored in a column name.</p>
<p>The following snippet demonstrates reading the name and phone number from
our phone number query:</p>
<pre>
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());
}
}
</pre>
<h3>Modifying Data<a name="modifyingdata" id="modifyingdata"></a></h3>
<p>To batch update a group of records (for example, to change "NY" to "New
York" in all contact fields), call the {@link
android.content.ContentResolver#update(android.net.Uri,android.content.ContentValues,java.lang.String,java.lang.String[]) ContentResolver.update()}
method with the columns and values to change.</p>
<h3>Adding a New Record <a name="addingrecord" id="addingrecord"></a></h3>
<p>To add a new record, call ContentResolver.insert() with the URI of the type
of item to add, and a Map of any values you want to set immediately on the new
record. This will return the full URI of the new record, including record
number, which you can then use to query and get a Cursor over the new
record.</p>
<pre>
ContentValues values = new ContentValues();
Uri phoneUri = null;
Uri emailUri = null;
values.put(Contacts.People.NAME, "New Contact");
//1 = the new contact is added to favorites
//0 = the new contact is not added to favorites
values.put(Contacts.People.STARRED,1);
//Add Phone Numbers
Uri uri = getContentResolver().insert(Contacts.People.CONTENT_URI, values);
//The best way to add Contacts data like Phone, email, IM is to
//get the CONTENT_URI of the contact just inserted from People's table,
//and use withAppendedPath to construct the new Uri to insert into.
phoneUri = Uri.withAppendedPath(uri, Contacts.People.Phones.CONTENT_DIRECTORY);
values.clear();
values.put(Contacts.Phones.TYPE, Phones.TYPE_MOBILE);
values.put(Contacts.Phones.NUMBER, "1233214567");
getContentResolver().insert(phoneUri, values);
//Add Email
emailUri = Uri.withAppendedPath(uri, ContactMethods.CONTENT_DIRECTORY);
values.clear();
//ContactMethods.KIND is used to distinguish different kinds of
//contact data like email, im, etc.
values.put(ContactMethods.KIND, Contacts.KIND_EMAIL);
values.put(ContactMethods.DATA, "test@example.com");
values.put(ContactMethods.TYPE, ContactMethods.TYPE_HOME);
getContentResolver().insert(emailUri, values);
</pre>
<p>To save a file, you can call ContentResolver().openOutputStream() with the
URI as shown in the following snippet:
</p>
<pre>
// Save the name and description in a map. Key is the content provider's
// column name, value is the value to save in that record field.
ContentValues values = new ContentValues(3);
values.put(MediaStore.Images.Media.DISPLAY_NAME, "road_trip_1");
values.put(MediaStore.Images.Media.DESCRIPTION, "Day 1, trip to Los Angeles");
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
// Add a new record without the bitmap, but with the values.
// It returns the URI of the new record.
Uri uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
try {
// Now get a handle to the file for that record, and save the data into it.
// sourceBitmap is a Bitmap object representing the file to save to the database.
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);
}
</pre>
<h3>Deleting a Record <a name="deletingrecord" id="deletingrecord"></a></h3>
<p>To delete a single record, call {@link
android.content.ContentResolver#delete(android.net.Uri,java.lang.String,java.lang.String[]) ContentResolver.delete()}
with the URI of a specific row.</p>
<p>To delete multiple rows, call ContentResolver.delete() with the URI of the
type of record to delete (for example,
android.provider.Contacts.People.CONTENT_URI) and a SQL WHERE clause defining
which rows to delete (<em>Warning</em>: be sure to include a valid WHERE
clause if deleting a general type using ContentResolver.delete(), or else you
risk deleting more records than you intended!).</p>
<h2>Creating a Content Provider<a name="creatingacontentprovider"
id="creatingacontentprovider"></a> </h2> <p>Here is how to create your own
content provider to act as a public source for reading and writing a new data
type:</p>
<ol>
<li>Extend
{@link android.content.ContentProvider ContentProvider}.</li>
<li>Define a <code>public static final {@link android.net.Uri Uri} </code>named
CONTENT_URI. This is the string that represents the full "content://" URI
that your content provider handles. You must define a unique string for this
value; the best solution is to use the fully-qualified class name of your
content provider (lowercase). So, for example: <br>
<code>public static final Uri CONTENT_URI = Uri.parse(
"content://com.google.codelab.rssprovider");</code></li>
<li>Create your system for storing data. Most content providers store their
data using Android's file storage methods or SQLite databases, but you can
store your data any way you want, so long as you follow the calling and return
value conventions.</li>
<li>Define the column names that you will return to your clients. If you are
using an underlying database, these column names are typically identical
to the SQL database column names they represent. In any case, you should include
an integer column named <code>_id</code> to
define a specific record number. If using the SQLite database, this should
be type <code>INTEGER
PRIMARY KEY AUTOINCREMENT</code>.
The <code>AUTOINCREMENT</code> descriptor
is optional, but by default, SQLite
autoincrements an ID counter field to the next number above the largest
existing number in the table. If you delete the last row, the next row added
will have the same ID as the deleted row. To avoid this by having SQLite
increment to the next largest value whether deleted or not, then assign your
ID column the following type: INTEGER PRIMARY KEY AUTOINCREMENT. (<strong>Note</strong> You
should have a unique _id field whether or not you have another field (such
as a URL) that is also unique among all records.) Android provides the
{@link android.database.sqlite.SQLiteOpenHelper SQLiteOpenHelper}
class to help you create and manage versions of your database. </li>
<li>If you are exposing byte data, such as a bitmap file, the field that stores
this data should actually be a string field with a content:// URI for that
specific file. This is the field that clients will call to retrieve this
data. The content provider for that content type (it can be the same content
provider or another content provider &mdash; for example, if you're storing a photo
you would use the media content provider) should implement a field named
_data for that record. The _data field lists the exact file path on the device
for that file. This field is not intended to be read by the client, but by
the ContentResolver. The client will call {@link
android.content.ContentResolver#openOutputStream(android.net.Uri) ContentResolver.openOutputStream()} on the user-facing field holding the URI
for the item (for example, the column named photo might have a value content://media/images/4453).
The ContentResolver will request the _data field for that record, and because
it has higher permissions than a client, it should be able to access
that file directly and return a read wrapper for that file to the client. </li>
<li>Declare public static
Strings that clients can use to specify which columns to return, or to specify
field values from the cursor. Carefully document the data type of each
field. Remember that file fields, such as audio or bitmap fields, are typically
returned as string path values </li>
<li>Return a {@link android.database.Cursor Cursor} object over a recordset in
reply to a query. This means implementing the query(), update(), insert(),
and delete() methods. As a courtesy, you might want to call {@link android.content.ContentResolver#notifyChange(android.net.Uri,android.database.ContentObserver) ContentResolver.notifyChange()} to notify
listeners about updated information. </li>
<li>Add a <code>&lt;provider&gt;</code> tag to AndroidManifest.xml, and use its
<em>authorities</em> attribute to define the authority part of the content type it should
handle. For example, if your content type is content://com.example.autos/auto
to request a list of all autos, then <em>authorities</em> would be <code>com.example.autos</code>.
Set the <em>multiprocess</em> attribute to true if data does not need to
be synchronized between multiple running versions of the content provider. </li>
<li>If you are handling a new data type, you must define a new MIME type to return
for your implementation of {@link android.content.ContentProvider#getType(android.net.Uri) android.ContentProvider.getType(url)}. This type corresponds to the <code>content://</code> URI
submitted to getType(), which will be one of the content types handled by
the provider. The MIME type for each content type has two forms: one for
a specific record, and one for multiple records. Use the {@link android.net.Uri Uri} methods to help determine what is being requested. Here is
the general format for each:
<ul>
<li><code>vnd.android.cursor.item/vnd.<em>yourcompanyname.contenttype</em></code>
for a single row. For example, a request for train record 122 using
<pre>content://com.example.transportationprovider/trains/122</pre>
might return the MIME type
<code>vnd.android.cursor.item/vnd.example.rail</code>
</li>
<li><code>vnd.android.cursor.dir/vnd.<em>yourcompanyname.contenttype</em></code>
for multiple rows. For example, a request for all train records using
<pre>content://com.example.transportationprovider/trains</pre>
might return the MIME type
<code>vnd.android.cursor.dir/vnd.example.rail</code>
</li>
</ul>
</li>
</ol>
<p>For an example of a private content provider implementation, see the NodePadProvider
class in the notepad sample application that ships with the SDK. </p>
<p>Here is a recap of the important parts of a content URI:</p>
<p><img src="../../images/content_uri.png" alt="Elements of a content URI" height="80" width="528"> </p>
<ol type="A">
<li>Standard required prefix. Never modified. </li>
<li>Authority part. For third-party applications, this should be a fully-qualified
class to ensure uniqueness. This corresponds to the value in
the <code>&lt;provider&gt;</code> element's <em>authorities</em> attribute:
<code>&lt;provider class="TransportationProvider"
authorities="com.example.transportationprovider"
/&gt;</code> </li>
<li>The path that the content provider uses to determine what kind of data is
being requested. This can be zero or more segments: if the content provider
exposes only one type of data (only trains, for example), this can be absent.
If it provides several types, including subtypes, this can be several elements
long: e.g., "<code>land/bus</code>, <code>land/train</code>, <code>sea/ship</code>,
and <code>sea/submarine</code>" to give four possibilities.</li>
<li>A specific record being requested, if any. This is the _id value of a specific
record being requested. If all records of a specific type are being requested,
omit this and the trailing slash: <code>content://com.example.transportationprovider/trains</code> </li>
</ol>