A content URI pattern matches content URIs using wildcard characters:
*
: Matches a string of any valid characters of any length.#
: Matches a string of numeric characters of any length.The following content URI patterns would be possible:
content://com.example.app.provider/*
content://com.example.app.provider/table2/*
:
dataset1
and
dataset2
, but doesn't match content URIs for
table1
or
table3
.
content://com.example.app.provider/table3/#
: Matches a content URI for single rows in
table3
, such as
content://com.example.app.provider/table3/6
for the row identified by
6
.
The following code snippet shows how the methods in UriMatcher
work. This code handles URIs for an entire table differently from URIs for a single row, by using the content URI pattern content://
for tables, and content://
for single rows.
The method addURI()
maps an authority and path to an integer value. The method match()
returns the integer value for a URI. A switch
statement chooses between querying the entire table, and querying for a single record:
public class ExampleProvider extends ContentProvider {
...
// Creates a UriMatcher object.
private static final UriMatcher sUriMatcher;
...
/*
* The calls to addURI() go here, for all of the content URI patterns that the provider
* should recognize. For this snippet, only the calls for table 3 are shown.
*/
...
/*
* Sets the integer value for multiple rows in table 3 to 1. Notice that no wildcard is used
* in the path
*/
sUriMatcher.addURI("com.example.app.provider", "table3", 1);
/*
* Sets the code for a single row to 2. In this case, the "#" wildcard is
* used. "content://com.example.app.provider/table3/3" matches, but
* "content://com.example.app.provider/table3 doesn't.
*/
sUriMatcher.addURI("com.example.app.provider", "table3/#", 2);
...
// Implements ContentProvider.query()
public Cursor query(
Uri uri,
String[] projection,
String selection,
String[] selectionArgs,
String sortOrder) {
...
/*
* Choose the table to query and a sort order based on the code returned for the incoming
* URI. Here, too, only the statements for table 3 are shown.
*/
switch (sUriMatcher.match(uri)) {
// If the incoming URI was for all of table3
case 1:
if (TextUtils.isEmpty(sortOrder)) sortOrder = "_ID ASC";
break;
// If the incoming URI was for a single row
case 2:
/*
* Because this URI was for a single row, the _ID value part is
* present. Get the last path segment from the URI; this is the _ID value.
* Then, append the value to the WHERE clause for the query
*/
selection = selection + "_ID = " uri.getLastPathSegment();
break;
default:
...
// If the URI is not recognized, you should do some error handling here.
}
// call the code to actually do the query
}
Another class,
ContentUris
, provides convenience methods for working with the
id
part of content URIs. The classes
Uri
and
Uri.Builder
include convenience methods for parsing existing
Uri
objects and building new ones.
The ContentProvider.query()
method must return a Cursor
object, or if it fails, throw an Exception
. If you are using an SQLite database as your data storage, you can simply return the Cursor
returned by one of the query()
methods of the SQLiteDatabase
class. If the query does not match any rows, you should return a Cursor
instance whose getCount()
method returns 0. You should return null
only if an internal error occurred during the query process.
If you aren't using an SQLite database as your data storage, use one of the concrete subclasses of Cursor
. For example, the MatrixCursor
class implements a cursor in which each row is an array of Object
. With this class, useaddRow()
to add a new row.
Remember that the Android system must be able to communicate the Exception
across process boundaries. Android can do this for the following exceptions that may be useful in handling query errors:
IllegalArgumentException
(You may choose to throw this if your provider receives an invalid content URI)NullPointerException
The insert()
method adds a new row to the appropriate table, using the values in the ContentValues
argument. If a column name is not in the ContentValues
argument, you may want to provide a default value for it either in your provider code or in your database schema.
This method should return the content URI for the new row. To construct this, append the new row's _ID
(or other primary key) value to the table's content URI, using withAppendedId()
.
The update()
method takes the same ContentValues
argument used by insert()
, and the same selection
andselectionArgs
arguments used by delete()
and ContentProvider.query()
. This may allow you to re-use code between these methods.
The delete()
method does not have to physically delete rows from your data storage. If you are using a sync adapter with your provider, you should consider marking a deleted row with a "delete" flag rather than removing the row entirely. The sync adapter can check for deleted rows and remove them from the server before deleting them from the provider.
The Android system calls onCreate()
when it starts up the provider. You should perform only fast-running initialization tasks in this method, and defer database creation and data loading until the provider actually receives a request for the data. If you do lengthy tasks in onCreate()
, you will slow down your provider's startup. In turn, this will slow down the response from the provider to other applications.
For example, if you are using an SQLite database you can create a new SQLiteOpenHelper
object inContentProvider.onCreate()
, and then create the SQL tables the first time you open the database. To facilitate this, the first time you call getWritableDatabase()
, it automatically calls the SQLiteOpenHelper.onCreate()
method.
ContentProvider.onCreate()
and
SQLiteOpenHelper.onCreate()
.
ContentProvider.onCreate()
:
public class ExampleProvider extends ContentProvider
/*
* Defines a handle to the database helper object. The MainDatabaseHelper class is defined
* in a following snippet.
*/
private MainDatabaseHelper mOpenHelper;
// Defines the database name
private static final String DBNAME = "mydb";
// Holds the database object
private SQLiteDatabase db;
public boolean onCreate() {
/*
* Creates a new helper object. This method always returns quickly.
* Notice that the database itself isn't created or opened
* until SQLiteOpenHelper.getWritableDatabase is called
*/
mOpenHelper = new MainDatabaseHelper(
getContext(), // the application context
DBNAME, // the name of the database)
null, // uses the default SQLite cursor
1 // the version number
);
return true;
}
...
// Implements the provider's insert method
public Cursor insert(Uri uri, ContentValues values) {
// Insert code here to determine which table to open, handle error-checking, and so forth
...
/*
* Gets a writeable database. This will trigger its creation if it doesn't already exist.
*
*/
db = mOpenHelper.getWritableDatabase();
}
}
SQLiteOpenHelper.onCreate()
, including a helper class:
...
// A string that defines the SQL statement for creating a table
private static final String SQL_CREATE_MAIN = "CREATE TABLE " +
"main " + // Table's name
"(" + // The columns in the table
" _ID INTEGER PRIMARY KEY, " +
" WORD TEXT"
" FREQUENCY INTEGER " +
" LOCALE TEXT )";
...
/**
* Helper class that actually creates and manages the provider's underlying data repository.
*/
protected static final class MainDatabaseHelper extends SQLiteOpenHelper {
/*
* Instantiates an open helper for the provider's SQLite data repository
* Do not do database creation and upgrade here.
*/
MainDatabaseHelper(Context context) {
super(context, DBNAME, null, 1);
}
/*
* Creates the data repository. This is called when the provider attempts to open the
* repository and SQLite reports that it doesn't exist.
*/
public void onCreate(SQLiteDatabase db) {
// Creates the main table
db.execSQL(SQL_CREATE_MAIN);
}
}
The ContentProvider
class has two methods for returning MIME types:
getType()
getStreamTypes()
The getType()
method returns a String
in MIME format that describes the type of data returned by the content URI argument. The Uri
argument can be a pattern rather than a specific URI; in this case, you should return the type of data associated with content URIs that match the pattern.
getType()
should return the standard MIME type for that data. A full list of these standard types is available on the IANA MIME Media Types
website.
·For content URIs that point to a row or rows of table data, getType()
should return a MIME type in Android's vendor-specific MIME format:
vnd
android.cursor.item/
android.cursor.dir/
vnd.
.
You supply the
and
. The
value should be globally unique, and the
value should be unique to the corresponding URI pattern. A good choice for
is your company's name or some part of your application's Android package name. A good choice for the
is a string that identifies the table associated with the URI.
For example, if a provider's authority is com.example.app.provider
, and it exposes a table named table1
, the MIME type for multiple rows in table1
is:
vnd.android.cursor.dir/vnd.com.example.provider.table1
For a single row of table1
, the MIME type is:
vnd.android.cursor.item/vnd.com.example.provider.table1
If your provider offers files, implement getStreamTypes()
. The method returns a String
array of MIME types for the files your provider can return for a given content URI. You should filter the MIME types you offer by the MIME type filter argument, so that you return only those MIME types that the client wants to handle.
For example, consider a provider that offers photo images as files in .jpg
, .png
, and .gif
format. If an application calls ContentResolver.getStreamTypes()
with the filter string image/*
(something that is an "image"), then theContentProvider.getStreamTypes()
method should return the array:
{ "image/jpeg", "image/png", "image/gif"}
If the app is only interested in .jpg
files, then it can call ContentResolver.getStreamTypes()
with the filter string*\/jpeg
, and ContentProvider.getStreamTypes()
should return:
{"image/jpeg"}
If your provider doesn't offer any of the MIME types requested in the filter string, getStreamTypes()
should returnnull
.
the important points are:
SQLiteDatabase
databases you create are private to your application and provider.If you want to use content provider permissions to control access to your data, then you should store your data in internal files, SQLite databases, or the "cloud" (for example, on a remote server), and you should keep files and databases private to your application.
All applications can read from or write to your provider, even if the underlying data is private, because by default your provider does not have permissions set. To change this, set permissions for your provider in your manifest file, using attributes or child elements of the
element. You can set permissions that apply to the entire provider, or to certain tables, or even to certain records, or all three.
You define permissions for your provider with one or more
elements in your manifest file. To make the permission unique to your provider, use Java-style scoping for the android:name
attribute. For example, name the read permission com.example.app.provider.permission.READ_PROVIDER
.
The following list describes the scope of provider permissions, starting with the permissions that apply to the entire provider and then becoming more fine-grained. More fine-grained permissions take precedence over ones with larger scope:
The following list describes the scope of provider permissions, starting with the permissions that apply to the entire provider and then becoming more fine-grained. More fine-grained permissions take precedence over ones with larger scope:
android:permission
attribute of the
element.
android:readPermission
and
android:writePermission
attributes of the
element. They take precedence over the permission required by
android:permission
.
child element of the
element. For each content URI you specify, you can specify a read/write permission, a read permission, or a write permission, or all three. The read and write permissions take precedence over the read/write permission. Also, path-level permission takes precedence over provider-level permissions.
Consider the permissions you need to implement an email provider and app, when you want to allow an outside image viewer application to display photo attachments from your provider. To give the image viewer the necessary access without requiring permissions, set up temporary permissions for content URIs for photos. Design your email app so that when the user wants to display a photo, the app sends an intent containing the photo's content URI and permission flags to the image viewer. The image viewer can then query your email provider to retrieve the photo, even though the viewer doesn't have the normal read permission for your provider.
To turn on temporary permissions, either set the android:grantUriPermissions
attribute of the
element, or add one or more
child elements to your
element. If you use temporary permissions, you have to call Context.revokeUriPermission()
whenever you remove support for a content URI from your provider, and the content URI is associated with a temporary permission.
The attribute's value determines how much of your provider is made accessible. If the attribute is set to true
, then the system will grant temporary permission to your entire provider, overriding any other permissions that are required by your provider-level or path-level permissions.
If this flag is set to false
, then you must add
child elements to your
element. Each child element specifies the content URI or URIs for which temporary access is granted.
To delegate temporary access to an application, an intent must contain theFLAG_GRANT_READ_URI_PERMISSION
or the FLAG_GRANT_WRITE_URI_PERMISSION
flags, or both. These are set with the setFlags()
method.
If the android:grantUriPermissions
attribute is not present, it's assumed to be false
.
Like Activity
and Service
components, a subclass of ContentProvider
must be defined in the manifest file for its application, using the
element. The Android system gets the following information from the element:
android:authorities
)
android:name
)
ContentProvider
. This class is described in more detail in the section Implementing the ContentProvider Class.
android:grantUriPermssions
: Temporary permission flag.android:permission
: Single provider-wide read/write permission.android:readPermission
: Provider-wide read permission.android:writePermission
: Provider-wide write permission.Permissions and their corresponding attributes are described in more detail in the section Implementing Content Provider Permissions.
android:enabled
: Flag allowing the system to start the provider.android:exported
: Flag allowing other applications to use this provider.android:initOrder
: The order in which this provider should be started, relative to other providers in the same process.android:multiProcess
: Flag allowing the system to start the provider in the same process as the calling client.android:process
: The name of the process in which the provider should run.android:syncable
: Flag indicating that the provider's data is to be sync'ed with data on a server.The attributes are fully documented in the dev guide topic for the
element.
android:icon
: A drawable resource containing an icon for the provider. The icon appears next to the provider's label in the list of apps in Settings > Apps > All.android:label
: An informational label describing the provider or its data, or both. The label appears in the list of apps in Settings > Apps > All.The attributes are fully documented in the dev guide topic for the
element.