您的位置:首页 > 移动开发 > Android开发

Content Provider Basics

2016-04-03 19:32 537 查看
A content provider manages access to a central repository of data. A provider is part of an Android application, which often provides its own UI for working with the data. However, content providers are primarily intended to be used by other applications, which
access the provider using a provider client object. Together, providers and provider clients offer a consistent, standard interface to data that also handles inter-process communication and secure data access.

This topic describes the basics of the following:

How content providers work.
The API you use retrieve data from a content provider.
The API you use to insert, update, or delete data in a content provider.
Other API features that facilitate working with providers.


Overview

A content provider presents data to external applications as one or more tables that are similar to the tables found in a relational database. A row represents an instance of some type of data the provider collects, and each column in the row represents an
individual piece of data collected for an instance.

For example, one of the built-in providers in the Android platform is the user dictionary, which stores the spellings of non-standard words that the user wants to keep. Table 1 illustrates what the data might look like in this provider's table:

Table 1: Sample user dictionary table.
wordapp idfrequencylocale_ID
mapreduceuser1100en_US1
precompileruser14200fr_FR2
appletuser2225fr_CA3
constuser1255pt_BR4
intuser5100en_UK5
In table 1, each row represents an instance of a word that might not be found in a standard dictionary. Each column represents some data for that word, such as the locale in which it was first encountered. The column headers are column names that are stored
in the provider. To refer to a row's locale, you refer to its
locale
column. For this provider, the
_ID
column serves as a "primary key" column that the provider automatically maintains.

Note: A provider isn't required to have a primary key, and it isn't required to use
_ID
as the column name of a primary key if one is present. However, if you want to bind data from a provider to a
ListView
,
one of the column names has to be
_ID
. This requirement is explained in more detail in the section Displaying
query results.


Accessing a provider

An application accesses the data from a content provider with a
ContentResolver
client
object. This object has methods that call identically-named methods in the provider object, an instance of one of the concrete subclasses of
ContentProvider
.
The
ContentResolver
methods provide the basic "CRUD" (create, retrieve, update,
and delete) functions of persistent storage.

The
ContentResolver
object in the client application's process and the
ContentProvider
object
in the application that owns the provider automatically handle inter-process communication.
ContentProvider
also
acts as an abstraction layer between its repository of data and the external appearance of data as tables.

Note: To access a provider, your application usually has to request specific permissions in its manifest file. This is described in more detail in the section Content
Provider Permissions

For example, to get a list of the words and their locales from the User Dictionary Provider, you call
, java.lang.String, java.lang.String[], java.lang.String)]ContentResolver.query()
.
The
, java.lang.String, java.lang.String[], java.lang.String)]query()
method
calls the
, java.lang.String, java.lang.String[], java.lang.String)]ContentProvider.query()
method
defined by the User Dictionary Provider. The following lines of code show a
, java.lang.String, java.lang.String[], java.lang.String)]ContentResolver.query()
call:

// Queries the user dictionary and returns results
mCursor = getContentResolver().query(
UserDictionary.Words.CONTENT_URI,   // The content URI of the words table
mProjection,                        // The columns to return for each row
mSelectionClause                    // Selection criteria
mSelectionArgs,                     // Selection criteria
mSortOrder);                        // The sort order for the returned rows


Table 2 shows how the arguments to
, java.lang.String, java.lang.String[], java.lang.String)]query(Uri,projection,selection,selectionArgs,sortOrder)
match
an SQL SELECT statement:

Table 2: Query() compared to SQL query.
query() argumentSELECT keyword/parameterNotes
Uri
FROM table_name
Uri
maps to the table in the provider named table_name.
projection
col,col,col,...
projection
is an array of columns that should be included for each row retrieved.
selection
WHERE col =value
selection
specifies the criteria for selecting rows.
selectionArgs
(No exact equivalent. Selection arguments replace
?
placeholders in the selection clause.)
sortOrder
ORDER BYcol,col,...
sortOrder
specifies the order in which rows appear in the returned
Cursor
.


Content URIs

A content URI is a URI that identifies data in a provider. Content URIs include the symbolic name of the entire provider (its authority) and a name that points to a table (a path). When you call a client method
to access a table in a provider, the content URI for the table is one of the arguments.

In the preceding lines of code, the constant
CONTENT_URI
contains
the content URI of the user dictionary's "words" table. The
ContentResolver
object
parses out the URI's authority, and uses it to "resolve" the provider by comparing the authority to a system table of known providers. The
ContentResolver
can
then dispatch the query arguments to the correct provider.

The
ContentProvider
uses the path part of the content URI to choose the table
to access. A provider usually has a path for each table it exposes.

In the previous lines of code, the full URI for the "words" table is:
content://user_dictionary/words


where the
user_dictionary
string is the provider's authority, and the
words
string is the table's path. The string
content://
(the scheme) is always present, and identifies this
as a content URI.

Many providers allow you to access a single row in a table by appending an ID value to the end of the URI. For example, to retrieve a row whose
_ID
is
4
from user dictionary, you can use this content URI:
Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4);


You often use id values when you've retrieved a set of rows and then want to update or delete one of them.

Note: The
Uri
and
Uri.Builder
classes
contain convenience methods for constructing well-formed URI objects from strings. The
ContentUris
class
contains convenience methods for appending id values to a URI. The previous snippet uses
withAppendedId()
to
append an id to the UserDictionary content URI.


Retrieving Data from the Provider

This section describes how to retrieve data from a provider, using the User Dictionary Provider as an example.

For the sake of clarity, the code snippets in this section call
, java.lang.String, java.lang.String[], java.lang.String)]ContentResolver.query()
on
the "UI thread"". In actual code, however, you should do queries asynchronously on a separate thread. One way to do this is to use the
CursorLoader
class,
which is described in more detail in the Loaders guide. Also, the lines of code are snippets only; they don't show
a complete application.

To retrieve data from a provider, follow these basic steps:

Request the read access permission for the provider.
Define the code that sends a query to the provider.


Requesting read access permission

To retrieve data from a provider, your application needs "read access permission" for the provider. You can't request this permission at run-time; instead, you have to specify that you need this permission in your manifest, using the
<uses-permission>
element
and the exact permission name defined by the provider. When you specify this element in your manifest, you are in effect "requesting" this permission for your application. When users install your application, they implicitly grant this request.

To find the exact name of the read access permission for the provider you're using, as well as the names for other access permissions used by the provider, look in the provider's documentation.

The role of permissions in accessing providers is described in more detail in the section Content
Provider Permissions.

The User Dictionary Provider defines the permission
android.permission.READ_USER_DICTIONARY
in its manifest file, so an application that wants to read from the provider must request this permission.


Constructing the query

The next step in retrieving data from a provider is to construct a query. This first snippet defines some variables for accessing the User Dictionary Provider:
// A "projection" defines the columns that will be returned for each row
String[] mProjection =
{
UserDictionary.Words._ID,    // Contract class constant for the _ID column name
UserDictionary.Words.WORD,   // Contract class constant for the word column name
UserDictionary.Words.LOCALE  // Contract class constant for the locale column name
};

// Defines a string to contain the selection clause
String mSelectionClause = null;

// Initializes an array to contain selection arguments
String[] mSelectionArgs = {""};


The next snippet shows how to use
, java.lang.String, java.lang.String[], java.lang.String)]ContentResolver.query()
,
using the User Dictionary Provider as an example. A provider client query is similar to an SQL query, and it contains a set of columns to return, a set of selection criteria, and a sort order.

The set of columns that the query should return is called a projection (the variable
mProjection
).

The expression that specifies the rows to retrieve is split into a selection clause and selection arguments. The selection clause is a combination of logical and Boolean expressions, column names, and values (the variable
mSelectionClause
).
If you specify the replaceable parameter
?
instead of a value, the query method retrieves the value from the selection arguments array (the variable
mSelectionArgs
).

In the next snippet, if the user doesn't enter a word, the selection clause is set to
null
, and the query returns all the words in the provider. If the user enters a word, the selection clause is set to
UserDictionary.Words.WORD
+ " = ?"
and the first element of selection arguments array is set to the word the user enters.
/*
* This defines a one-element String array to contain the selection argument.
*/
String[] mSelectionArgs = {""};

// Gets a word from the UI
mSearchString = mSearchWord.getText().toString();

// Remember to insert code here to check for invalid or malicious input.

// If the word is the empty string, gets everything
if (TextUtils.isEmpty(mSearchString)) {
// Setting the selection clause to null will return all words
mSelectionClause = null;
mSelectionArgs[0] = "";

} else {
// Constructs a selection clause that matches the word that the user entered.
mSelectionClause = UserDictionary.Words.WORD + " = ?";

// Moves the user's input string to the selection arguments.
mSelectionArgs[0] = mSearchString;

}

// Does a query against the table and returns a Cursor object
mCursor = getContentResolver().query(
UserDictionary.Words.CONTENT_URI,  // The content URI of the words table
mProjection,                       // The columns to return for each row
mSelectionClause                   // Either null, or the word the user entered
mSelectionArgs,                    // Either empty, or the string the user entered
mSortOrder);                       // The sort order for the returned rows

// Some providers return null if an error occurs, others throw an exception
if (null == mCursor) {
/*
* Insert code here to handle the error. Be sure not to use the cursor! You may want to
* call android.util.Log.e() to log this error.
*
*/
// If the Cursor is empty, the provider found no matches
} else if (mCursor.getCount() < 1) {

/*
* Insert code here to notify the user that the search was unsuccessful. This isn't necessarily
* an error. You may want to offer the user the option to insert a new row, or re-type the
* search term.
*/

} else {
// Insert code here to do something with the results

}


This query is analogous to the SQL statement:
SELECT _ID, word, locale FROM words WHERE word = <userinput> ORDER BY word ASC;


In this SQL statement, the actual column names are used instead of contract class constants.


Protecting against malicious input

If the data managed by the content provider is in an SQL database, including external untrusted data into raw SQL statements can lead to SQL injection.

Consider this selection clause:
// Constructs a selection clause by concatenating the user's input to the column name
String mSelectionClause =  "var = " + mUserInput;


If you do this, you're allowing the user to concatenate malicious SQL onto your SQL statement. For example, the user could enter "nothing; DROP TABLE *;" for
mUserInput
, which would result in the selection clause
var = nothing;
DROP TABLE *;
. Since the selection clause is treated as an SQL statement, this might cause the provider to erase all of the tables in the underlying SQLite database (unless the provider is set up to catch SQL
injection attempts).

To avoid this problem, use a selection clause that uses
?
as a replaceable parameter and a separate array of selection arguments. When you do this, the user input is bound directly to the query rather than being interpreted as part of
an SQL statement. Because it's not treated as SQL, the user input can't inject malicious SQL. Instead of using concatenation to include the user input, use this selection clause:
// Constructs a selection clause with a replaceable parameter
String mSelectionClause =  "var = ?";


Set up the array of selection arguments like this:
// Defines an array to contain the selection arguments
String[] selectionArgs = {""};


Put a value in the selection arguments array like this:
// Sets the selection argument to the user's input
selectionArgs[0] = mUserInput;


A selection clause that uses
?
as a replaceable parameter and an array of selection arguments array are preferred way to specify a selection, even if the provider isn't based on an SQL database.


Displaying query results

The
, java.lang.String, java.lang.String[], java.lang.String)]ContentResolver.query()
client
method always returns a
Cursor
containing the columns specified by the query's projection
for the rows that match the query's selection criteria. A
Cursor
object provides random
read access to the rows and columns it contains. Using
Cursor
methods, you can iterate
over the rows in the results, determine the data type of each column, get the data out of a column, and examine other properties of the results. Some
Cursor
implementations
automatically update the object when the provider's data changes, or trigger methods in an observer object when the
Cursor
changes,
or both.

Note: A provider may restrict access to columns based on the nature of the object making the query. For example, the Contacts Provider restricts access for some columns to sync adapters, so it won't return them to an activity or service.

If no rows match the selection criteria, the provider returns a
Cursor
object for which
Cursor.getCount()
is
0 (an empty cursor).

If an internal error occurs, the results of the query depend on the particular provider. It may choose to return
null
, or it may throw an
Exception
.

Since a
Cursor
is a "list" of rows, a good way to display the contents of a
Cursor
is
to link it to a
ListView
via a
SimpleCursorAdapter
.

The following snippet continues the code from the previous snippet. It creates a
SimpleCursorAdapter
object
containing the
Cursor
retrieved by the query, and sets this object to be the adapter
for a
ListView
:
// Defines a list of columns to retrieve from the Cursor and load into an output row
String[] mWordListColumns =
{
UserDictionary.Words.WORD,   // Contract class constant containing the word column name
UserDictionary.Words.LOCALE  // Contract class constant containing the locale column name
};

// Defines a list of View IDs that will receive the Cursor columns for each row
int[] mWordListItems = { R.id.dictWord, R.id.locale};

// Creates a new SimpleCursorAdapter
mCursorAdapter = new SimpleCursorAdapter(
getApplicationContext(),               // The application's Context object
R.layout.wordlistrow,                  // A layout in XML for one row in the ListView
mCursor,                               // The result from the query
mWordListColumns,                      // A string array of column names in the cursor
mWordListItems,                        // An integer array of view IDs in the row layout
0);                                    // Flags (usually none are needed)

// Sets the adapter for the ListView
mWordList.setAdapter(mCursorAdapter);


Note: To back a
ListView
with a
Cursor
,
the cursor must contain a column named
_ID
. Because of this, the query shown previously retrieves the
_ID
column for the "words" table, even though the
ListView
doesn't
display it. This restriction also explains why most providers have a
_ID
column for each of their tables.


Getting data from query results

Rather than simply displaying query results, you can use them for other tasks. For example, you can retrieve spellings from the user dictionary and then look them up in other providers. To do this, you iterate over the rows in the
Cursor
:
// Determine the column index of the column named "word"
int index = mCursor.getColumnIndex(UserDictionary.Words.WORD);

/*
* Only executes if the cursor is valid. The User Dictionary Provider returns null if
* an internal error occurs. Other providers may throw an Exception instead of returning null.
*/

if (mCursor != null) {
/*
* Moves to the next row in the cursor. Before the first movement in the cursor, the
* "row pointer" is -1, and if you try to retrieve data at that position you will get an
* exception.
*/
while (mCursor.moveToNext()) {

// Gets the value from the column.
newWord = mCursor.getString(index);

// Insert code here to process the retrieved word.

...

// end of while loop
}
} else {

// Insert code here to report an error if the cursor is null or the provider threw an exception.
}


Cursor
implementations contain several "get" methods for retrieving different types of
data from the object. For example, the previous snippet uses
getString()
.
They also have a
getType()
method that returns a value indicating the data
type of the column.


Content Provider Permissions

A provider's application can specify permissions that other applications must have in order to access the provider's data. These permissions ensure that the user knows what data an application will try to access. Based on the provider's requirements, other
applications request the permissions they need in order to access the provider. End users see the requested permissions when they install the application.

If a provider's application doesn't specify any permissions, then other applications have no access to the provider's data. However, components in the provider's application always have full read and write access, regardless of the specified permissions.

As noted previously, the User Dictionary Provider requires the
android.permission.READ_USER_DICTIONARY
permission to retrieve data from it. The provider has the separate
android.permission.WRITE_USER_DICTIONARY
permission
for inserting, updating, or deleting data.

To get the permissions needed to access a provider, an application requests them with a
<uses-permission>
element
in its manifest file. When the Android Package Manager installs the application, a user must approve all of the permissions the application requests. If the user approves all of them, Package Manager continues the installation; if the user doesn't approve
them, Package Manager aborts the installation.

The following
<uses-permission>
element requests read access to the User
Dictionary Provider:
<uses-permission android:name="android.permission.READ_USER_DICTIONARY">


The impact of permissions on provider access is explained in more detail in the Security and Permissions guide.


Inserting, Updating, and Deleting Data

In the same way that you retrieve data from a provider, you also use the interaction between a provider client and the provider's
ContentProvider
to
modify data. You call a method of
ContentResolver
with arguments that are passed
to the corresponding method of
ContentProvider
. The provider and provider client
automatically handle security and inter-process communication.


Inserting data

To insert data into a provider, you call the
ContentResolver.insert()
method.
This method inserts a new row into the provider and returns a content URI for that row. This snippet shows how to insert a new word into the User Dictionary Provider:
// Defines a new Uri object that receives the result of the insertion
Uri mNewUri;

...

// Defines an object to contain the new values to insert
ContentValues mNewValues = new ContentValues();

/*
* Sets the values of each column and inserts the word. The arguments to the "put"
* method are "column name" and "value"
*/
mNewValues.put(UserDictionary.Words.APP_ID, "example.user");
mNewValues.put(UserDictionary.Words.LOCALE, "en_US");
mNewValues.put(UserDictionary.Words.WORD, "insert");
mNewValues.put(UserDictionary.Words.FREQUENCY, "100");

mNewUri = getContentResolver().insert(
UserDictionary.Word.CONTENT_URI,   // the user dictionary content URI
mNewValues                          // the values to insert
);


The data for the new row goes into a single
ContentValues
object, which is similar
in form to a one-row cursor. The columns in this object don't need to have the same data type, and if you don't want to specify a value at all, you can set a column to
null
using
ContentValues.putNull()
.

The snippet doesn't add the
_ID
column, because this column is maintained automatically. The provider assigns a unique value of
_ID
to every row that is added. Providers usually use this value as the table's primary
key.

The content URI returned in
newUri
identifies the newly-added row, with the following format:
content://user_dictionary/words/<id_value>


The
<id_value>
is the contents of
_ID
for the new row. Most providers can detect this form of content URI automatically and then perform the requested operation on that particular row.

To get the value of
_ID
from the returned
Uri
, call
ContentUris.parseId()
.


Updating data

To update a row, you use a
ContentValues
object with the updated values just as
you do with an insertion, and selection criteria just as you do with a query. The client method you use is
)]ContentResolver.update()
.
You only need to add values to the
ContentValues
object for columns you're updating.
If you want to clear the contents of a column, set the value to
null
.

The following snippet changes all the rows whose locale has the language "en" to a have a locale of
null
. The return value is the number of rows that were updated:
// Defines an object to contain the updated values
ContentValues mUpdateValues = new ContentValues();

// Defines selection criteria for the rows you want to update
String mSelectionClause = UserDictionary.Words.LOCALE +  "LIKE ?";
String[] mSelectionArgs = {"en_%"};

// Defines a variable to contain the number of updated rows
int mRowsUpdated = 0;

...

/*
* Sets the updated value and updates the selected words.
*/
mUpdateValues.putNull(UserDictionary.Words.LOCALE);

mRowsUpdated = getContentResolver().update(
UserDictionary.Words.CONTENT_URI,   // the user dictionary content URI
mUpdateValues                       // the columns to update
mSelectionClause                    // the column to select on
mSelectionArgs                      // the value to compare to
);


You should also sanitize user input when you call
)]ContentResolver.update()
.
To learn more about this, read the section Protecting against malicious input.


Deleting data

Deleting rows is similar to retrieving row data: you specify selection criteria for the rows you want to delete and the client method returns the number of deleted rows. The following snippet deletes rows whose appid matches "user". The method returns the number
of deleted rows.
// Defines selection criteria for the rows you want to delete
String mSelectionClause = UserDictionary.Words.APP_ID + " LIKE ?";
String[] mSelectionArgs = {"user"};

// Defines a variable to contain the number of rows deleted
int mRowsDeleted = 0;

...

// Deletes the words that match the selection criteria
mRowsDeleted = getContentResolver().delete(
UserDictionary.Words.CONTENT_URI,   // the user dictionary content URI
mSelectionClause                    // the column to select on
mSelectionArgs                      // the value to compare to
);


You should also sanitize user input when you call
)]ContentResolver.delete()
.
To learn more about this, read the section Protecting against malicious input.


Provider Data Types

Content providers can offer many different data types. The User Dictionary Provider offers only text, but providers can also offer the following formats:

integer
long integer (long)
floating point
long floating point (double)

Another data type that providers often use is Binary Large OBject (BLOB) implemented as a 64KB byte array. You can see the available data types by looking at the
Cursor
class
"get" methods.

The data type for each column in a provider is usually listed in its documentation. The data types for the User Dictionary Provider are listed in the reference documentation for its contract class
UserDictionary.Words
(contract
classes are described in the section Contract Classes). You can also
determine the data type by calling
Cursor.getType()
.

Providers also maintain MIME data type information for each content URI they define. You can use the MIME type information to find out if your application can handle data that the provider offers, or to choose a type of handling based on the MIME type. You
usually need the MIME type when you are working with a provider that contains complex data structures or files. For example, the
ContactsContract.Data
table
in the Contacts Provider uses MIME types to label the type of contact data stored in each row. To get the MIME type corresponding to a content URI, call
ContentResolver.getType()
.

The section MIME Type Reference describes the syntax of both standard
and custom MIME types.


Alternative Forms of Provider Access

Three alternative forms of provider access are important in application development:

Batch access: You can create a batch of access calls
with methods in the
ContentProviderOperation
class, and then apply
them with
ContentResolver.applyBatch()
.
Asynchronous queries: You should do queries in a separate thread. One way to do this is to use a
CursorLoader
object.
The examples in the Loaders guide demonstrate how to do this.
Data access via intents: Although you can't send
an intent directly to a provider, you can send an intent to the provider's application, which is usually the best-equipped to modify the provider's data.

Batch access and modification via intents are described in the following sections.


Batch access

Batch access to a provider is useful for inserting a large number of rows, or for inserting rows in multiple tables in the same method call, or in general for performing a set of operations across process boundaries as a transaction (an atomic operation).

To access a provider in "batch mode", you create an array of
ContentProviderOperation
objects
and then dispatch them to a content provider with
ContentResolver.applyBatch()
.
You pass the content provider'sauthority to this method, rather than a particular content URI. This allows each
ContentProviderOperation
object
in the array to work against a different table. A call to
ContentResolver.applyBatch()
returns
an array of results.

The description of the
ContactsContract.RawContacts
contract class
includes a code snippet that demonstrates batch insertion. The Contact Manager sample application contains
an example of batch access in its
ContactAdder.java
source file.


Displaying data using a helper app

If your application does have access permissions, you still may want to use an intent to display data in another application. For example, the Calendar
application accepts an
ACTION_VIEW
intent, which displays
a particular date or event. This allows you to display calendar information without having to create your own UI. To learn more about this feature, see the Calendar
Provider guide.
The application to which you send the intent doesn't have to be the application associated with the provider. For example, you can retrieve a contact from the Contact Provider, then send an
ACTION_VIEW
intent
containing the content URI for the contact's image to an image viewer.


Data access via intents

Intents can provide indirect access to a content provider. You allow the user to access data in a provider even if your application doesn't have access permissions, either by getting a result intent back from an application that has permissions, or by activating
an application that has permissions and letting the user do work in it.


Getting access with temporary permissions

You can access data in a content provider, even if you don't have the proper access permissions, by sending an intent to an application that does have the permissions and receiving back a result intent containing "URI" permissions. These are permissions for
a specific content URI that last until the activity that receives them is finished. The application that has permanent permissions grants temporary permissions by setting a flag in the result intent:

Read permission:
FLAG_GRANT_READ_URI_PERMISSION

Write permission:
FLAG_GRANT_WRITE_URI_PERMISSION


Note: These flags don't give general read or write access to the provider whose authority is contained in the content URI. The access is only for the URI itself.

A provider defines URI permissions for content URIs in its manifest, using the
android:grantUriPermission
attribute
of the
<provider>
element, as well as the
<grant-uri-permission>
child
element of the
<provider>
element. The URI permissions mechanism is explained in
more detail in the Security and Permissions guide, in the section "URI Permissions".

For example, you can retrieve data for a contact in the Contacts Provider, even if you don't have the
READ_CONTACTS
permission.
You might want to do this in an application that sends e-greetings to a contact on his or her birthday. Instead of requesting
READ_CONTACTS
,
which gives you access to all of the user's contacts and all of their information, you prefer to let the user control which contacts are used by your application. To do this, you use the following process:

Your application sends an intent containing the action
ACTION_PICK
and
the "contacts" MIME type
CONTENT_ITEM_TYPE
, using
the method
startActivityForResult()
.
Because this intent matches the intent filter for the People app's "selection" activity, the activity will come to the foreground.
In the selection activity, the user selects a contact to update. When this happens, the selection activity calls
setResult(resultcode,
intent)
to set up a intent to give back to your application. The intent contains the content URI of the contact the user selected, and the "extras" flags
FLAG_GRANT_READ_URI_PERMISSION
.
These flags grant URI permission to your app to read data for the contact pointed to by the content URI. The selection activity then calls
finish()
to
return control to your application.
Your activity returns to the foreground, and the system calls your activity's
onActivityResult()
method.
This method receives the result intent created by the selection activity in the People app.
With the content URI from the result intent, you can read the contact's data from the Contacts Provider, even though you didn't request permanent read access permission to the provider in your manifest. You can then get the contact's
birthday information or his or her email address and then send the e-greeting.


Using another application

A simple way to allow the user to modify data to which you don't have access permissions is to activate an application that has permissions and let the user do the work there.

For example, the Calendar application accepts an
ACTION_INSERT
intent, which
allows you to activate the application's insert UI. You can pass "extras" data in this intent, which the application uses to pre-populate the UI. Because recurring events have a complex syntax, the preferred way of inserting events into the Calendar Provider
is to activate the Calendar app with an
ACTION_INSERT
and then let the
user insert the event there.


Contract Classes

A contract class defines constants that help applications work with the content URIs, column names, intent actions, and other features of a content provider. Contract classes are not included automatically with a provider; the provider's developer has to define
them and then make them available to other developers. Many of the providers included with the Android platform have corresponding contract classes in the package
android.provider
.

For example, the User Dictionary Provider has a contract class
UserDictionary
containing
content URI and column name constants. The content URI for the "words" table is defined in the constant
UserDictionary.Words.CONTENT_URI
.
The
UserDictionary.Words
class also contains column name constants, which
are used in the example snippets in this guide. For example, a query projection can be defined as:
String[] mProjection =
{
UserDictionary.Words._ID,
UserDictionary.Words.WORD,
UserDictionary.Words.LOCALE
};


Another contract class is
ContactsContract
for the Contacts Provider. The reference
documentation for this class includes example code snippets. One of its subclasses,
ContactsContract.Intents.Insert
,
is a contract class that contains constants for intents and intent data.


MIME Type Reference

Content providers can return standard MIME media types, or custom MIME type strings, or both.

MIME types have the format
type/subtype


For example, the well-known MIME type
text/html
has the
text
type and the
html
subtype. If the provider returns this type for a URI, it means that a query using that URI will return text containing
HTML tags.

Custom MIME type strings, also called "vendor-specific" MIME types, have more complex type and subtypevalues. The type value is always
vnd.android.cursor.dir


for multiple rows, or
vnd.android.cursor.item


for a single row.

The subtype is provider-specific. The Android built-in providers usually have a simple subtype. For example, when the Contacts application creates a row for a telephone number, it sets the following MIME type in the row:
vnd.android.cursor.item/phone_v2


Notice that the subtype value is simply
phone_v2
.

Other provider developers may create their own pattern of subtypes based on the provider's authority and table names. For example, consider a provider that contains train timetables. The provider's authority is
com.example.trains
, and it
contains the tables Line1, Line2, and Line3. In response to the content URI

content://com.example.trains/Line1


for table Line1, the provider returns the MIME type
vnd.android.cursor.dir/vnd.example.line1


In response to the content URI
content://com.example.trains/Line2/5


for row 5 in table Line2, the provider returns the MIME type
vnd.android.cursor.item/vnd.example.line2


Most content providers define contract class constants for the MIME types they use. The Contacts Provider contract class
ContactsContract.RawContacts
,
for example, defines the constant
CONTENT_ITEM_TYPE
for
the MIME type of a single raw contact row.

Content URIs for single rows are described in the section Content URIs.

内容提供者管理存取数据的中央存储库。提供者是一个Android应用程序,它通常提供自己的用户界面与数据工作的一部分。但是,内容提供者主要是用来由其他应用程序,该访问使用提供商客户对象的提供者使用。总之,供应商和供应商的客户提供一致的,标准的接口将数据,也负责处理进程间通信和安全的数据访问。

本主题介绍了以下的基本知识:

如何内容提供商的工作。
该API使用从内容提供商检索数据。
该API使用的内容提供商插入,更新或删除数据。
促进与提供商合作,其他API功能。


概观

一种内容提供者呈现的数据向外部应用程序作为类似于在关系数据库中找到的表的一个或多个表。中的一行表示某种类型的数据的提供者收集的一个实例,并且该行中的每一列表示收集的实例数据的一个单独的片。

例如,在Android平台内置提供商之一是用户字典,其中存储的该用户想要保持非标准字的拼写。表1列出了数据可能看起来像在此提供的表格:

表1:示例用户字典表。
应用程序ID频率区域_ID
mapreduce的USER1100EN_US1
预编译器user14200在fr_FR2
小程序用户2225fr_CA3
常量USER1255PT_BR4
INTUSER5100en_UK
在表1中,每一行代表可能不是标准字典中找到的单词的实例。每一列代表该字的一些数据,如在其被第一次遇到的区域设置。列标题是存储在提供商列名。要引用行的区域,你是指其
语言环境
列。对于此提供程序,
_ID
列作为“主键”一栏提供程序自动维护。

注:不是必需的供应商,有一个主键,并且它使用不需要
_ID
作为主键的列名如果存在。但是,如果你想从一个供应商到数据绑定
ListView中
,列名称之一已被
_id
。此要求中有更详细的部分中说明显示查询结果


访问提供商

一个应用程序从与内容提供商访问数据
ContentResolver的
客户对象。这个对象调用的提供者对象同名的方法,对具体子类的一个实例方法
的ContentProvider
。该
ContentResolver的
方法提供了基本的“CRUD”(创建,检索,更新和删除)的持久存储功能。

ContentResolver的
对象在客户端应用程序的进程和
ContentProvider的
在拥有提供商自动处理进程间通信。应用程序对象
的ContentProvider
也作为其数据存储库和数据的外观为表之间的抽象层。

注意:要访问提供商,应用程序通常有要求的清单文件中特定的权限。此进行更详细的部分中所述 内容提供者的权限

例如,要得到的话,并从用户词典提供的语言环境的列表,你叫
, java.lang.String, java.lang.String[], java.lang.String)]ContentResolver.query() 
。该
, java.lang.String, java.lang.String[], java.lang.String)]查询()
方法调用
, java.lang.String, java.lang.String[], java.lang.String)]ContentProvider.query()
的用户词典提供商定义的方法。下面的代码行显示一个
, java.lang.String, java.lang.String[], java.lang.String)]ContentResolver.query()
调用:

//查询用户词典并返回结果
mCursor = getContentResolver ()查询(
UserDictionary 。词。CONTENT_URI ,   //字表的内容URI
mProjection ,                        //列返回的每一行
mSelectionClause                     //选择标准
mSelectionArgs ,                     //选择标准
mSortOrder );                         //的排序顺序返回的行


表2显示了参数
, java.lang.String, java.lang.String[], java.lang.String)]查询(URI,投影,选择,selectionArgs两个,中将sortOrder)
匹配一个SQL
SELECT语句:

表2:查询()相比,SQL查询。
查询()说法SELECT关键字/参数笔记
乌里
FROMTABLE_NAME
乌里
映射到表中提供名为table_name的。
投影
山坳,山坳,山坳,...
投影
是应该包括用于检索每一行列的阵列。
选择
WHERE COL = 价值
选择
指定用于选择行的标准。
selectionArgs两个
(不完全等效。选择论取代
选择子句中的占位符。)
排序
ORDER BY 山坳,山坳,...
中将sortOrder
指定哪些行出现在返回的顺序
光标


内容的URI

一个内容URI是一个URI,在供应商标识数据。内容的URI包括整个提供商(其的符号名称权威)和指向的表(一个名称路径)。当你调用客户端的方法来访问一个供应商一个表,表的内容是URI的参数之一。

在代码的先前行中,恒定
CONTENT_URI
包含用户辞典的“字”表的内容的URI。该
ContentResolver的
对象分析了URI的授权,并使用它经权威机构对比已知供应商的系统表来“解决”的提供者。该
ContentResolver的
话可以调度查询参数正确的供应商。

ContentProvider的
使用内容的URI的路径部分来选择表格的访问。提供者通常有一个路径为它暴露出各表。

在代码的行以前,完整的URI“单词”表:
内容:// user_dictionary /字


其中
user_dictionary
字符串是供应商的权力和
文字
字符串表的路径。该字符串
的内容://
(该方案)始终存在,并确定这是一个内容URI。

许多供应商则允许通过附加一个ID值的URI的结尾来访问表中的一行。例如,要检索行的
_ID
4
从用户词典,您可以使用此内容URI:
乌里singleUri =  ContentUris 。withAppendedId (UserDictionary 。词。CONTENT_URI ,4 );


你经常使用的ID值,当你检索到的一组行,然后要更新或删除其中之一。

注:
乌里
Uri.Builder
类包含从字符串的建设,形成良好的URI对象的便捷方法。该
ContentUris
类包含附加ID值到URI方便的方法。上面的代码片段使用
withAppendedId()
以一个id追加到UserDictionary内容URI。


从提供者检索数据

本节将介绍如何从一个供应商使用用户词典提供商作为一个例子检索数据。

为了清楚起见,本节中的代码片段调用
, java.lang.String, java.lang.String[], java.lang.String)]ContentResolver.query()
的“UI线程”,“关于在实际的代码,但是,你应该做的查询异步在一个单独的线程。这样做的一个方法是使用的
CursorLoader
类,它被更详细地所描述 装载机。导向此外,代码行是唯一的代码段;他们不显示完整的应用程序。

从供应商获取数据,请按照下列基本步骤:

请求提供者的读取访问权限。
定义发送一个查询到供应商的代码。


请求读取访问权限

从供应商获取数据,您的应用需求“读访问权限”,为供应商。你不能要求在运行时此权限; 相反,你必须指定你需要在你的清单此权限,使用
<使用许可权>
元素,由提供者定义确切的权限名称。当您在清单中指定此元素,你实际上是“要求”此权限的应用程序。当用户安装应用程序,他们含蓄同意这一请求。

要找到适合你使用的供应商,以及由供应商使用的其他访问权限的名字的读访问权限的确切名称,看供应商的文档中获得。

权限在访问提供商的作用进行更详细的部分中所述 内容提供者的权限

用户词典提供者定义权限
android.permission.READ_USER_DICTIONARY
在其清单文件,所以想要从提供商读取必须要求此权限的应用程序。


构建查询

在从提供者检索数据的下一个步骤是建立一个查询。这第一个片段定义了访问用户词典提供一些变量:
//一个“投影”定义,将针对每行返回的列
的String [] mProjection =
{
UserDictionary 。词。_ID ,    //合同类常数_ID列名
UserDictionary 。词,WORD ,   //合同类常数字列名
UserDictionary 。词。LOCALE   //合同类常量的语言环境列名
};

//定义一个字符串包含选择条款
字符串mSelectionClause =  空;

//初始化数组包含选择论
的String [] mSelectionArgs =  { “” };


接下来的片段展示了如何使用
, java.lang.String, java.lang.String[], java.lang.String)]ContentResolver.query() 
,使用用户词典提供商作为一个例子。提供者客户端查询类似于SQL查询,它包含了一组列返回,一套选择标准和排序顺序。

设定该查询应返回的列被称为一个凸起 (变量
mProjection
)。

指定的行检索表达式被分成选择条款和选择的参数。在选择条款是合乎逻辑和布尔表达式,列名和值(变量的组合
mSelectionClause
)。如果指定替换参数
而不是一个值,查询方法从选择参数数组值(变量
mSelectionArgs
)。

在接下来的片断,如果用户没有输入一个字,选择子句设置为
,且查询返回供应商的所有单词。如果用户输入一个字,该选择子句设置为
UserDictionary.Words.WORD +“=?”
和选择参数的数组的第一元素被设置为用户输入的单词。
/ *
*定义一个一个元素的字符串数组包含选择参数。
* /
字符串[] mSelectionArgs =  { “” };

//获取从UI一句话
mSearchString = mSearchWord 。gettext的()。的toString ();

/ /记住在这里插入代码来检查无效或恶意输入。

//如果单词是空字符串,得到的一切
,如果 (文本实用程序。的isEmpty (mSearchString )) {
//将选择条款设置为null将返回所有文字
mSelectionClause =  空;
mSelectionArgs [ 0 ]  =  “” ;

}  其他 {
//构造一个用户输入的字词相匹配的选择条款
mSelectionClause =  UserDictionary 。词。WORD +  “=”?;

//移动用户的输入字符串的选择参数。
mSelectionArgs [ 0 ]  = mSearchString ;

}

//是否对表的查询,并返回一个Cursor对象
mCursor = getContentResolver 。()查询(
UserDictionary 。词。CONTENT_URI ,  //字表的内容URI
mProjection ,                       / /列返回的每一行
mSelectionClause                    //为NULL,或单词用户输入
mSelectionArgs ,                    //要么是空或字符串用户输入
mSortOrder );                        //的排序顺序返回行

//一些供应商的回报空如果出现错误,其他人抛出一个异常
,如果 (空 == mCursor ) {
/ *
*此处插入代码来处理错误。一定不要使用游标!你可能想
*调用android.util.Log.e()来记录这个错误。
*
* /
//如果光标是空的,供应商没有找到匹配
}  否则 如果 (mCursor 。getCount将() <  1 ) {

/ *
*在这里插入代码以通知搜索是不成功的用户。这不一定
*错误。您可能希望向用户提供的选项来插入新行,或重新输入
*搜索词。
* /

}  其他 {
//此处插入代码做的结果的东西

}


该查询类似于SQL语句:
SELECT _ID ,文字,语言环境从文字WHERE字=  <userinput> ORDER BY ASC字;


在这条SQL语句,实际列名来代替合同类常量。


防止恶意输入

如果由内容提供商管理的数据是在SQL数据库中,包括外部不可信数据成原始的SQL语句可导致SQL注入。

考虑这个选择条款:
//通过连接用户输入列名构造一个选择条款
字符串mSelectionClause =   “VAR =”  + mUserInput ;


如果你这样做,你让用户来连接恶意SQL到你的SQL语句。例如,用户可以输入“什么; DROP TABLE *;” 对于
mUserInput
,这将导致在选择条款
变种=无; DROP TABLE *;
。由于选择子句作为SQL语句处理,这可能会导致供应商删除所有表的底层SQLite数据库(除非供应商设置了捕捉 SQL注入尝试)。

为了避免这个问题,使用使用选择条款
作为可替换参数和参数选择一个单独的数组。执行此操作时,用户输入直接绑定到查询,而不是被解释为SQL语句的一部分。因为它不是为SQL处理,用户输入无法注入恶意SQL。而不是使用级联包括用户输入,使用此选择条款:
//构造一个具有可替换参数选择条款
字符串mSelectionClause =   “VAR =?” ;


设置的参数选择这样的数组:
//定义数组包含选择论
的String [] selectionArgs两个=  { “” };


把一个价值选择的参数数组像这样:
//将选择参数传递给用户的输入
selectionArgs两个[ 0 ]  = mUserInput ;


使用一个选择条款
作为可替换参数和选择参数数组的数组是首选的方法来指定一个选择,即使供应商是不是基于SQL数据库上。


显示查询结果

, java.lang.String, java.lang.String[], java.lang.String)]ContentResolver.query()
客户端的方法总是返回
游标
包含由查询的预测为匹配查询的选择标准,该行指定的列。一个
游标
对象提供它所包含的行和列的随机读取访问。使用
游标
的方法,你可以遍历在结果中的行,确定每一列的数据类型,获取数据的列,并检查结果的其他属性。一些
光标
时在观察者对象的提供者的数据的变化,或触发方法时实现自动更新对象
的光标
的变化,或两者兼而有之。

注意:提供者可以限制对基于使查询对象的性质列。例如,联系人提供商限制某些列同步适配器接入,所以它不会返回他们的活动或服务。

如果没有相匹配的行的选择标准,提供者将返回一个
游标
对象这
Cursor.getCount()
0(空光标)。

如果发生内部错误时,查询的结果取决于特定的提供者。它可以选择返回
空值
,否则可能抛出
异常


由于
光标
是一个行的“名单”,一个很好的方式来显示内容
光标
是将其链接到
的ListView
通过
SimpleCursorAdapter


下面的代码片段继续从前面的代码片断中的代码。它创造了一个
SimpleCursorAdapter
包含对象
光标
查询检索,并将这个对象是一个适配器
的ListView

//定义列的列表,从游标检索和加载到一个输出行
的String [] mWordListColumns =
{
UserDictionary 。词,WORD ,   //包含单词列名合同类常量
UserDictionary 。词。LOCALE   //合同类常量包含的语言环境列名
};

//定义将接收光标列的每一行查看ID列表
INT [] mWordListItems =  { - [R 。ID 。dictWord ,ř 。ID 。区域};

//创建一个新的SimpleCursorAdapter
mCursorAdapter =  新 SimpleCursorAdapter (
getApplicationContext ()               //应用程序的快捷对象
- [R 。布局。wordlistrow ,                  // XML中的布局一行在ListView
mCursor ,                               //从查询结果
mWordListColumns ,                      //列一个字符串数组在游标名
mWordListItems ,                        //在该行布局视图ID的整数数组
0 );                                     //标志(需要通常是没有)

//设置为ListView适配器
mWordList 。setAdapter (mCursorAdapter );


注意:要备份一个
ListView的
一个
光标
,光标必须包含一个名为列
_ID
。正因为如此,先前显示的查询检索
_ID
列中“字”的表,即使
ListView中
不显示它。此限制也解释了为什么大多数供应商有一个
_ID
它们的每个表的列。


充分利用查询结果数据

而不是简单地显示查询结果,你可以将它们用于其他任务。例如,你可以从用户词典中的拼写,然后看看他们在其他供应商。要做到这一点,你迭代中的行
光标

//确定命名为“字”列的列索引
INT 指数= mCursor 。getColumnIndex (UserDictionary 。词。WORD );

/ *
*只有当光标是有效的执行。用户词典提供商返回如果空
*发生内部错误。其他供应商可能会抛出异常,而不是返回null。
* /

如果 (mCursor !=  空) {
/ *
*移动到光标下一行。光标第一乐章前,
*“行指针”是-1,如果你尝试在该位置检索数据,你会得到一个
*例外。
* /
时 (mCursor 。moveToNext ()) {

//获取从列的值。
newWord = mCursor 。的getString (指数);

//在这里插入代码来处理检索词。

......

// while循环结束
}
}  其他 {

//此处插入代码,如果光标报告错误为空或供应商抛出异常。
}


光标
实现包含用于从对象获取不同类型的数据的多个“获取”方法。例如,上面的代码片段使用
的getString()
。它们还具有
的getType()
返回指示该列的数据类型的值的方法。


内容提供商权限

提供者的应用程序可以指定其他应用程序必须有为了访问提供者的数据的权限。这些权限确保用户知道什么数据的应用程序将试图访问。根据供应商的要求,其他的应用程序请求他们为了访问提供商需要的权限。最终用户看到的请求的权限时,他们安装应用程序。

如果一个提供商的应用程序不指定任何权限,那么其他应用程序的供应商的数据的访问权限。然而,在供应商的应用程序组件总是有充分的读写访问,无论指定的权限。

如前面所指出的,用户字典提供者要求
android.permission.READ_USER_DICTIONARY
从中检索数据的权限。该供应商拥有独立
android.permission.WRITE_USER_DICTIONARY
插入,更新或删除数据的权限。

为了获得访问提供所需的权限,应用程序请求他们一个
<使用许可权>
在其清单文件中的元素。当Android的包管理器安装该应用程序,用户必须批准所有权限的应用程序的请求。如果用户批准所有的人,包管理器继续安装; 如果用户没有批准它们,包管理中止安装。

以下
<使用许可权>
元素的请求读取访问用户词典提供者:
<使用许可权 的android:名称= “android.permission.READ_USER_DICTIONARY” >


关于提供程序的访问权限的影响在更详细地解释 安全和权限指南。


插入,更新和删除数据

在您从提供商检索数据以同样的方式,也可以使用一个供应商客户和提供者的间的相互作用
的ContentProvider
修改数据。您呼叫的方法
ContentResolver的
与传递给对应的方法参数
的ContentProvider
。供应商和供应商客户端自动处理安全和进程间通信。


插入数据

将数据插入到一个提供者,你调用
ContentResolver.insert()
方法。这种方法插入一个新行到供应商,并返回该行的内容URI。这个片断显示了如何插入一个新词到用户词典提供者:
//定义接收插入的结果,一个新的URI对象
的Uri mNewUri ;

...

//定义一个对象包含新插入值
ContentValues ​​mNewValues ​​=  新 ContentValues ​​();

/ *
*设置每列的值,插入字。到“放”的争论
*的方法是“列名”和

用户词典内容URI
mNewValues                           ​​//插入值
);


为新的行的数据进入一个单一
ContentValues
​​对象,这是在形式到一行光标类似。在这个对象的列不必有相同的数据类型,如果你不希望在所有指定值,可以设置一栏
ContentValues.putNull() 


该片段不加
_ID
列,因为此列是自动维护。该供应商的独特价值分配
_ID
到添加的每一行。通常供应商使用这个值作为表的主键。

URI中返回的内容
newUri
识别新添加的行,但有以下格式:
内容:// user_dictionary /字/ <ID_VALUE>


<ID_VALUE>
是内容的
_ID
新行。大多数提供商可以自动检测这种形式的内容URI,然后在那个特定的行执行所请求的操作。

要获取的价值
_ID
从返回
的Uri
,调用
ContentUris.parseId() 


更新数据

要更新行,你用
ContentValues
​​用更新的价值观,正如你插入做的,选择标准,就像你用做查询对象。您使用的客户端的方法是
)]ContentResolver.update() 
。你只需要值添加到
ContentValues
​​对象要更新列。如果你想清除列的内容,设置为值


下面的代码片段改变所有的区域都有语言“EN”到有一个区域的行
。返回值是更新行数:
//定义一个对象来包含更新值
ContentValues ​​mUpdateValues ​​=  新 ContentValues ​​();

//定义选择标准要更新的行
字符串mSelectionClause =  UserDictionary 。词。LOCALE +   “怎么样?” ;
的String [] mSelectionArgs =  { “EN_%” };

//定义一个变量包含更新的行的数目
INT mRowsUpdated =  0 ;

...

/ *
*设定更新后的值,并更新选定的

用户词典内容URI
mUpdateValues                        ​​//列更新
mSelectionClause                     //列在选择
mSelectionArgs                       //值来比较
);


你也应该过滤用户输入,当你调用
)]ContentResolver.update() 
。要了解更多关于这一点,阅读部分保护儿童不受恶意输入


删除数据

删除行类似于检索行数据:您指定选择标准要删除的行和客户端方法返回删除的行数。下面的代码片段删除行的APPID匹配“用户”。该方法返回删除的行的数量。
//定义要删除的行选择标准
字符串mSelectionClause =  UserDictionary 。词。APP_ID +  “怎么样?” ;
的String [] mSelectionArgs =  { “用户” };

//定义一个变量包含的行数删除
INT mRowsDeleted =  0 ;

...

//删除符合选择条件的话
mRowsDeleted = getContentResolver ()。删除(
UserDictionary 。词。CONTENT_URI ,   //用户词典内容URI
mSelectionClause                     //列在选择
mSelectionArgs                       //将值来比较
);


你也应该过滤用户输入,当你调用
)]ContentResolver.delete() 
。要了解更多关于这一点,阅读部分保护儿童不受恶意输入


提供数据类型

内容提供商可以提供很多不同的数据类型。用户词典提供商只提供文字,而且供应商也可以提供以下格式:


长整型(长)
浮点
长浮点数(双)

该供应商经常使用的另一个数据类型是作为一个64KB字节数组执行二进制大对象(BLOB)。您可以通过查看查看可用的数据类型
光标
级“get”方法。

在供应商每列的数据类型通常是其文档中列出。为用户提供词典的数据类型为它的契约类的参考文档中列出
UserDictionary.Words
(合同类部分所述合同类)。您也可以通过调用确定数据类型
Cursor.getType() 


供应商还保持每个内容的URI它们定义的MIME数据类型信息。您可以使用MIME类型的信息,以找出是否您的应用程序能够处理的数据提供程序提供,还是要选择一个类型的处理基于MIME类型的。你通常需要MIME类型,当你正在使用包含复杂的数据结构或文件的供应商合作。例如,
ContactsContract.Data
在联系人提供商表使用MIME类型来标记存储在每行中的联系人数据的类型。为了获得对应于内容的URI的MIME类型,调用
ContentResolver.getType() 


本节MIME类型参考描述了标准和自定义MIME类型的语法。


提供接入的替代形式

供应商接入三种可供选择的形式在应用发展的重要:

批量访问:您可以创建一个批处理与方法访问调用
ContentProviderOperation
类,然后与应用它们
ContentResolver.applyBatch() 

异步查询:你应该做的查询,在一个单独的线程。这样做的一种方式是使用一个
CursorLoader
对象。在中的例子 装载机指南演示如何做到这一点。
通过意向数据访问:虽然你不能直接发送意图提供商,您可以发送意图供应商的应用程序,它通常是最好的装备来修改供应商的数据。

通过批量意图访问和修改在下面的章节中描述。


访问批

到提供者批次访问是用于插入大量的行,或插入在多个表中的行相同的方法调用,或在一般用于进行跨进程边界的一组操作的一个事务有用(一个原子操作)。

要访问“批处理模式”的提供者,为您打造一个数组
ContentProviderOperation
对象,然后将它们分发到内容提供商与
ContentResolver.applyBatch() 
。传递内容提供者的权限,以这种方法,而不是一个特定的内容的URI。这允许每个
ContentProviderOperation
阵列中对象针对不同的表工作。要在通话
ContentResolver.applyBatch()
返回结果的数组。

的的描述
ContactsContract.RawContacts
合同类包含的代码片段演示批量插入。该 联系人管理器 示例应用程序包含在其批次访问的示例
ContactAdder.java
源文件。


使用一个辅助的应用程序中显示数据

如果你的应用确实具有访问权限,你可能仍然要使用意图在其他应用程序中显示的数据。例如,日历应用程序接受一个
ACTION_VIEW
意图,其中显示在特定日期或事件。这可以让你无需创建自己的UI显示日历信息。要了解更多关于此功能,请参阅 日历提供指南。
向其发送的意图的应用程序不具有要与提供商相关联的应用。例如,你可以从联系人提供联系人,然后发送
ACTION_VIEW
包含内容的URI联系人的图像的图像浏览器的意图。


意图通过数据访问

意图可以提供给内容提供商间接访问。您让用户,可以通过得到的结果意图从具有权限的应用程序恢复访问数据提供者,即使你的应用程序没有访问权限,或激活具有权限的应用程序,让用户做工作,它。


获得临时访问权限

您可以通过发送一个意图确实有权限的应用程序和接收回包含“URI”权限,因此意图,内容提供商访问数据,即使你没有适当的访问权限。这些都是对于持续到接收他们完成的活动具体内容URI的权限。拥有永久许可应用程序通过在结果意图设置标志授予临时权限:

阅读权限:
FLAG_GRANT_READ_URI_PERMISSION

写权限:
FLAG_GRANT_WRITE_URI_PERMISSION


注意:这些标志不给一般的读取或写入访问其权威的内容中包含的URI的提供者。接入仅用于该URI本身。

提供者定义的URI权限在其清单内容的URI,使用
安卓grantUriPermission
的属性
<提供商>
元素,还有
<赠款URI的权限>
的子元素
<提供商>
元素。该URI的权限机制,在更详细的解释 安全和权限指导,参见“URI权限”。

例如,您可以在联系人提供的联系人检索数据,即使你没有
READ_CONTACTS
权限。您可能要为此在发送电子贺卡给他或她的生日联 ​​系人的应用程序。而不是请求的
READ_CONTACTS
,这使您可以访问到所有用户的联系人和他们的所有信息,你宁愿让这些联系人应用程序所使用的用户控件。要做到这一点,您可以使用以下过程:

您的应用程序发送包含动作的通知
ACTION_PICK
和“人脉”MIME类型
CONTENT_ITEM_TYPE
,使用方法
startActivityForResult() 

由于这种意图对联系人应用的“评选”活动的意图过滤器相匹配,该活动将来到前台。
在选择活动,用户选择联系人更新。发生这种情况时,该评选活动调用
的setResult(resultCode为,意图)
建立一个意图退给你的应用程序。这样做的目的包含了用户选择的接触,“额外”标志的内容URI
FLAG_GRANT_READ_URI_PERMISSION
。这些标志授予URI允许你的应用程序的联系人指向的内容URI读取数据。然后,将评选活动调用
完成()
将控制返回给应用程序。
您的活动返回到前台,系统调用你的活动的
的onActivityResult()
方法。这个方法接收由评选活动在人民创建的应用程序的结果意图。
从结果意图内容URI,您可以从联系人提供读取联系人数据,即使你没有请求永久读取访问权限,以在清单中的供应商。然后,您可以得到联系人的生日信息,或者他或她的电子邮件地址,然后发送电子贺卡。


使用其他应用程序

一个简单的方法,让用户修改到您没有访问权限的数据是激活具有权限的应用程序,让用户做的工作有。

例如,日历应用程序接受一个
ACTION_INSERT
意图,它允许您激活应用程序的插入UI。你可以通过“额外”数据在这个意图,该应用程序使用预先填充的UI。由于经常性的事件有复杂的语法,插入事件到日历提供商的最佳方法是使用激活日历应用
ACTION_INSERT
,然后让用户插入事件出现。


合同类

合同类定义帮助应用程序与内容的URI,列名,故意行为,和内容提供商等功能的工作常量。合同类不与提供者自动包括; 供应商的开发人员来定义它们,然后将它们提供给其他开发商。许多包含在Android平台上的供应商在包装相应的合同类
android.provider


例如,用户词典提供商签有合同类
UserDictionary
包含内容的URI和列名常量。对于“词”表中的内容URI在不断的定义
UserDictionary.Words.CONTENT_URI
。该
UserDictionary.Words
类也包含列名的常量,这是在例如段所使用本指南的 例如,查询的投影可被定义为:
String [] mProjection =
{
UserDictionary . Words . _ID ,
UserDictionary . Words . WORD ,
UserDictionary . Words . LOCALE
};


另一份合同类是
ContactsContract
的联系人提供商。这个类的参考文档包括示例代码片段。它的一个子类的
ContactsContract.Intents.Insert
,是包含意图和意图数据常量合同类。


MIME类型参考

内容提供商可以返回标准MIME媒体类型,或自定义的MIME类型的字符串,或两者。

MIME类型的格式
类型 / 子类型


例如,著名的MIME类型
为text / html
文本
类型和
HTML
亚型。如果提供者返回此类型的URI,则意味着使用这个URI将返回包含文本的HTML标记的查询。

自定义的MIME类型的字符串,也称为“特定供应商的”MIME类型,具有更复杂的类型和子类型的值。该类型值总是
越南盾。机器人,光标。DIR


多行或
越南盾。机器人,光标。项目


为单行。

该子类型是供应商特定的。而Android内置的供应商通常有一个简单的子类型。例如,当联系人应用程序创建用于电话号码的行,它设置该行中的下列MIME类型:
越南盾。机器人,光标。项目/ phone_v2


请注意,子类值只是
phone_v2


其他供应商的开发人员可以创建自己的基于供应商的权威和表名亚型的格局。例如,考虑包含列车时刻表的供应商。提供者的权威是
com.example.trains
,它包含的表1号线,2号线,3号线和。响应于内容的URI

内容://com.example.trains/Line1


为表1号线,提供者将返回的MIME类型
越南盾。机器人,光标。DIR / 越南盾。例如,第1行


响应于内容的URI
内容://com.example.trains/Line2/5


在2号线表列5,提供者将返回的MIME类型
越南盾。机器人,光标。项目 / 越南盾。例如,2号线


大多数内容提供商定义他们所使用的MIME类型合同类常量。联系人提供合同类
ContactsContract.RawContacts
,例如,定义了恒定
CONTENT_ITEM_TYPE
MIME类型的单一原料接触排。

单行内容的URI的章节中描述 的内容的URI
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息