Content providers manage access to structured data sets. They encapsulate data and provide a mechanism for defining data security. Content providers are standard interfaces that connect data in one process to code running in another.
If you want to access data in the content provider, you can use the ContentResolver object in the application Context as a client to communicate with the provider. The ContentResolver object communicates with the provider object, the class instance that implements the ContentProvider. The provider object receives the data request from the client, performs the requested action, and returns the result.
Android itself includes content providers that manage data such as audio, video, images and personal contact information.
Content providers present data to external applications in the form of one or more tables. A row represents an instance of a data type collected by the provider, and each column in the row represents each piece of data collected by the instance.
One of Android’s built-in providers is the user dictionary, which stores the spellings of non-standard words that the user wants to save.
Access provider
The application accesses data from a content provider that has a ContentResolver client object. This object has a method that calls a method of the same name in the provider object (an instance of a concrete subclass of ContentProvider). The ContentResolver method provides basic “CRUD” (create, retrieve, update, and delete) functionality for continuous storage.
The ContentResolver object in a client application process and the ContentProvider object in an application that has a provider handle cross-process communication automatically. A ContentProvider can also act as an abstraction layer between its data store and the external display of data in tabular form.
For example, to get a list of words and their locale from the user dictionary provider, call contentresolver.query (). The query() method calls the contentProvider.query () method defined by the user dictionary provider. The following line shows the 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
Copy the code
The content URI
The content URI is the URI used to identify data in the provider. The content URI includes the symbolic name of the entire provider (its authorization) and a name pointing to the table (path).
In the previous line of code, the constant CONTENT_URI contains the content URI of the “word” table of the user dictionary. The ContentResolver object analyzes the authorization of the URI and “resolves” the provider by comparing it to the system table of the known provider. The ContentResolver can then dispatch the query parameters to the correct provider.
The ContentProvider uses the path portion of the content URI to select the table to access. Providers typically display a path for each table they expose.
In the previous line of code, the full URI for the “words” table is:
content://user_dictionary/words
Copy the code
Where the user_Dictionary string is the provider’s authorization and the Words string is the path to the table. The string content:// (schema) is always displayed and identifies this as the 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 a user dictionary, use this content URI:
Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4);
Copy the code
The ID value is often used when you want to update or delete a row after retrieving a set of rows.
Retrieve data from the provider
To retrieve data from the provider, follow these basic steps:
- Request read access to the provider.
- Defines the code that sends queries to providers.
Request read access
To retrieve data from a provider, your provider should have “read access” rights to the provider. You cannot request this permission at run time; Instead, you need to specify in the manifest file that you need this permission using the < uses-Permission > element and the exact permission name defined by the provider. By specifying this element in your manifest file, you will effectively “request” this permission for your application. This request is implicitly granted when the user installs your application.
The user dictionary provider defines the permission Android.permissibility.READ_USER_DICTIONARY in its manifest file, so applications that want to read from the provider must request this permission.
To build a query
The first code snippet defines some variables used to access 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 = {""};
Copy the code
The next code snippet uses the user dictionary provider as an example to show how to use contentresolver.query (). Provider client queries are similar to SQL queries and contain a set of columns to return, a set of selection criteria, and a sort order.
The set of columns that a query should return is called a projection (variable mProjection).
The expression used to specify the row to be retrieved is split into selection clauses and selection parameters. A selection clause is a combination of logical and Boolean expressions, column names, and values (the variable mSelectionClause). What if you specified a replaceable parameter? Instead of a value, the query method retrieves the value from the selection parameter array (variable mSelectionArgs).
In the next code snippets, if no words are entered by the user, the selection clause is set to NULL, and the query returns all words in the provider. If the user input the Words, will be set to the select clause UserDictionary. Words. The WORD + “=?” And the first element of the selection parameters array is set to the word entered by the user.
/*
* 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
}
Copy the code
This query is similar to an SQL statement:
SELECT _ID, word, locale FROM words WHERE word = <userinput> ORDER BY word ASC;
Copy the code
Display query results
The contentResolver.query () client-side method always returns a Cursor that contains a projection of the query that specifies a column for the row matching the query selection criteria. The Cursor object provides random read access to the rows and columns it contains. Using the Cursor method, you can iterate over rows in a result, determine the data type of each column, retrieve data from the column, and examine other properties of the result. Some Cursor implementations automatically update the object when the provider’s data changes and/or trigger methods in the watch program object when the Cursor changes.
// 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);Copy the code
Get data from query results
// 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 rowin 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.
}
Copy the code
The Cursor implementation contains multiple “get” methods for retrieving different types of data from objects. For example, the previous code snippet used 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 the permissions necessary for other applications to access the provider’s data. These permissions ensure that the user knows what data the application is trying to access. Based on the provider’s request, other applications request the permissions they need to access the provider. The end user will see the requested permissions when the application is installed.
If the provider’s application does not specify any permissions, other applications will not have access to the provider’s data. However, components in a provider’s application always have full read and write access, regardless of the specified permissions.
As mentioned earlier, the user dictionary provider requires the Android.permission.READ_USER_DICTIONARY permission to retrieve data from it. The provider has a separate Android.permission.WRITE_USER_DICTIONARY permission for inserting, updating, or deleting data.
To obtain the permissions needed to access the provider, the application requests these permissions through the < uses-Permission > element in its manifest file. When the Android Package manager installs an application, the user must approve all permissions requested by the application. If the user approves all permissions, the package manager installation continues; If the user does not approve these permissions, the package manager aborts the installation.
The following
element requests read access to the user dictionary provider:
<uses-permission android:name="android.permission.READ_USER_DICTIONARY">
Copy the code
Insert, update, and delete data
Insert data
To insert data into the provider, call the contentresolver.insert () method. This method inserts a new line in the provider and returns a content URI for that line. This code snippet shows how to insert a new term 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
);
Copy the code
The data for the new row goes into a single ContentValues object, which is formally similar to a single-row cursor. The columns in this object do not need to have the same data type, and if you do not want to specify a value, you can set the column to null using contentValues.putnull ().
The code snippet does not add the _ID column because it is automatically maintained. The provider assigns a unique _ID value to each added row. Typically, providers use this value as the primary key of the table.
The content URI returned in newUri identifies the newly added row in the following format:
content://user_dictionary/words/<id_value>
Copy the code
is the _ID of the new row. Most providers can automatically detect a content URI in this format and then perform the requested action on that particular line.
To get the value of the _ID from the returned Uri, call contenturis.parseId ().
Update the data
To update the row, use the ContentValues object with the updated value as you perform the insert, and use the selection criteria as you perform the query. The client-side method you use is contentresolver.update (). You simply add the value to the ContentValues object of the column you want to update. If you want to clear the contents of the column, set the value to NULL.
The following code snippet changes the locale to NULL for all rows that have the language “en” in the locale. The return value is the number of updated rows:
// 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 );Copy the code
Delete the data
Deleting rows is similar to retrieving row data: Specify selection criteria for rows to be deleted, and the client method returns the number of deleted rows. The following code snippet removes the line where the app ID matches “user”. This 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 );Copy the code
Provider data type
Content providers can provide many different data types. The user dictionary provider provides only text, but the provider can also provide the following formats:
- The integer
- Long type (long)
- floating-point
- Long floating point (double)
Another data type frequently used by providers is binary Large objects (BLOBs) implemented as 64KB-byte arrays.
The provider also maintains MIME (Multipurpose Internet Mail Extension) data type information for each content URI it defines. You can use the MIME type information to find out whether the application can process the data provided by the provider, or to select a processing type based on the MIME type. MIME types are often required when using providers that contain complex data structures or files. For example, the ContactsContract.data table in the contact provider uses MIME types to mark the contact Data types stored in each row. To get the MIME type corresponding to the content URI, call contentResolver.getType ().
MIME type reference
Content providers can return standard MIME media types and/or custom MIME type strings.
MIME types have formats
type/subtype
Copy the code
The well-known MIME type text/ HTML has a text type and an HTML subtype. If the provider returns this type for the URI, it means that queries using the URI will return text containing HTML tags.
Custom MIME type strings (also known as vendor-specific MIME types) have more complex types and subtype values. The type value is always
vnd.android.cursor.dir
Copy the code
(multiple lines) or
vnd.android.cursor.item
Copy the code
(Single line).
Subtypes are provider specific. Android built-in providers usually have simple subtypes. For example, when the Contacts application creates a row for a phone number, it sets the following MIME types in the row:
vnd.android.cursor.item/phone_v2
Copy the code
Note that the subtype value is just phone_v2.
Other provider developers may create their own subtype schema based on the provider’s authorization and table name. For example, suppose the provider contains a train schedule. The provider is authorized by com.example.trains and contains the tables Line1, Line2, and Line3. The content URI in the response table Line1
content://com.example.trains/Line1
Copy the code
The provider returns the MIME type
vnd.android.cursor.dir/vnd.example.line1
Copy the code
The content URI in line 5 of the response table Line2
content://com.example.trains/Line2/5
Copy the code
The provider returns the MIME type
vnd.android.cursor.item/vnd.example.line2
Copy the code
Most content providers define protocol class constants for the MIME types they use. For example, the contacts provider agreement class ContactsContract. RawContacts will be for a single original contact lines of constant CONTENT_ITEM_TYPE the MIME type definition.
Alternative form of provider access
Three alternative forms of provider access are important in application development:
- Bulk access: you can create a batch of access calls through methods in the ContentProviderOperation class, and then through ContentResolver. ApplyBatch () apply them.
- Asynchronous query: You should execute the query in a separate thread. One way to do this is to use a CursorLoader object. The example in the loader guide shows how to do this.
- Access data through IntEnts: Although you can’t send an Intent directly to a provider, you can send an Intent to the provider’s application, which usually has the best configuration to modify the provider’s data.
The bulk access
Bulk access providers are suitable for inserting a large number of rows, or inserting rows in multiple tables through the same method call, or generally for performing a set of operations as transactions (atomic operations) across process boundaries.
To access provider under the “batch mode”, you can create a ContentProviderOperation array of objects, and then use the ContentResolver. ApplyBatch () will be assigned to the content provider. You need to pass authorization for the content provider to this method, not a specific content URI. This makes each ContentProviderOperation object in the array applicable to other tables. Call ContentResolver. ApplyBatch () will return the result array.
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); . int rawContactInsertIndex = ops.size(); ops.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI) .withValue(RawContacts.ACCOUNT_TYPE, accountType) .withValue(RawContacts.ACCOUNT_NAME, accountName) .build()); ops.add(ContentProviderOperation.newInsert(Data.CONTENT_URI) .withValueBackReference(Data.RAW_CONTACT_ID, rawContactInsertIndex) .withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE) .withValue(StructuredName.DISPLAY_NAME,"Mike Sullivan")
.build());
getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
Copy the code
Agreement class
The protocol class defines constants that help the application use content URIs, column names, Intent actions, and other features of the content provider. The protocol class is not automatically included in the provider; The developer of the provider needs to define them and then make them available to other developers.
For example, the UserDictionary provider has a protocol class UserDictionary that contains content uris and column name constants. “Words” table of contents URI in constant UserDictionary. Words. Define CONTENT_URI. The UserDictionary.words class also contains column name constants, which are used in the sample code snippet of this guide. For example, a query projection can be defined as:
String[] mProjection =
{
UserDictionary.Words._ID,
UserDictionary.Words.WORD,
UserDictionary.Words.LOCALE
};
Copy the code
Access data through intEnts
Intents can provide indirect access to content providers. Even if your application does not have access permissions, you can allow users to access data in a provider by getting a resulting Intent back from a privileged application, or by activating a privileged application and letting the user work in it.
Obtain access by temporary permission
Even if you don’t have the appropriate access permissions, you can access data in a content provider by sending an Intent to a privileged application and then receiving the resulting Intent back with the “URI” permission. These are permissions for specific content URIs that last until the Activity that receives them ends. An application with permanent permissions grants temporary permissions by setting flags in the resulting Intent:
Read permission: FLAG_GRANT_READ_URI_PERMISSION Write permission: FLAG_GRANT_WRITE_URI_PERMISSIONCopy the code
Provider using the < provider > element android: grantUriPermission attributes, and < provider > elements < grant – uri – permission > child element defined in the listing file content uri uri permissions.
For example, you can retrieve the data for a contact in the contact provider even if you don’t have READ_CONTACTS permission. You might want to do this in an application that sends electronic birthday wishes to contacts. Rather than asking READ_CONTACTS to give you access to all of the user’s contacts and their information, you would prefer to let the user control the contacts used by the application. To do this, you need to use the following process:
- Your application uses the method startActivityForResult() to send an Intent object containing the action ACTION_PICK and the “contact” MIME type CONTENT_ITEM_TYPE.
- Because this Intent matches the Intent filter of the Contact app’s “Select” Activity, the Activity is displayed in the foreground.
- In the Select Activity, the user selects the contact to update. When this happens, the selected Activity calls setResult(ResultCode, Intent) to set the intent to return to the application. The Intent contains the content URI of the contact selected by the user and the “extra” flag, FLAG_GRANT_READ_URI_PERMISSION. These flags grant your application URI permission to read the contact data pointed to by the content URI. Selecting the Activity then calls Finish (), giving control back to your application.
- Your Activity is returned to the foreground, and the system calls your Activity’s onActivityResult() method. This method receives the resulting Intent created by selecting the Activity in the Contacts application.
- With the content URI from the resulting Intent, you can read the contact data from the contact provider, even if you don’t request permanent read access to that provider in the manifest file. You can get a contact’s birthday information or E-mail address and send an e-greeting.
Use other apps
A simple way to allow users to modify data that you don’t have access to is to activate a privileged application and let the user perform work in it.
For example, a calendar application accepts an ACTION_INSERT Intent object, which lets you activate the application’s insert UI. You can pass “extra” data in this Intent, which the application will use to prepopulate the UI. Due to the complex syntax of periodic events, the preferred way to insert events into a calendar provider is to activate a calendar application with ACTION_INSERT and then have the user insert events into it.
Create a content provider
- Design raw storage for your data. Content providers provide data in two ways:
The file data
Data usually stored in files, such as photos, audio, or video. Store files in the private space of your application. Your provider can provide file handles in response to file requests from other applications.
“Structured” data
Data typically stored in a database, array, or similar structure. Store data as a compatible row table. Rows represent entities, such as people or inventory items. Columns represent some piece of data for an entity, such as the name of a person or the price of a good. Such data is typically stored in an SQLite database, but you can use any type of persistent storage.
- Define the concrete implementation of the ContentProvider class and its required methods. This class is the interface between your data and the rest of the Android system.
- Defines the provider’s authorization string, its content URI, and column name. If you want the provider’s application to handle the Intent, you also define the Intent action, Extra data, and flags. In addition, define the permissions that applications that want to access your data must have. You should consider defining all of these values as constants in a separate protocol class; You can later expose this class to other developers.
Design data store
A content provider is an interface for accessing data stored in a structured format.
Here are some of the data storage technologies available in Android:
The Android system includes a SQLite database API that Android’s own providers use to store table-oriented data. The SQLiteOpenHelper class helps you create the database, and the SQLiteDatabase class is the base class for accessing the database. Remember, you don’t have to use a database to implement the store. Externally, the provider appears as a set of tables, similar to a relational database, but this is not a requirement for the provider’s internal implementation;
To store file data, Android provides a variety of file-oriented apis.
To use web-based data, use classes in Java.net and Android.net.
Data design considerations
Here are some tips for designing provider data structures:
Table data should always have a “primary key” column that the provider maintains as a unique numeric value corresponding to each row. You can use this value to link this row to related rows in other tables (as a “foreign key”). Although you can use any name for this column, basecolumns._id is the best choice because the condition for linking the results of the provider query to the ListView is that the name of one of the retrieved columns must be _ID;
If you want to provide bitmap images or other very large file-oriented data, store the data in a file and then provide it indirectly rather than directly in a table. If you do this, you need to tell the provider’s users that they need to access the data using the ContentResolver file method;
Use binary Large object (BLOB) data types to store data that can change in size or structure. For example, you can use BLOB columns to store protocol buffers or JSON structures.
You can also use BLOBS to implement architecture-independent tables. In such a table, you need to define a primary key column, a MIME type column, and one or more generic columns in BLOB form. The meaning of the data in these BLOB columns is indicated by the values in the MIME type column. This way, you can store different types of rows in the same table. For example, the contact provider’s “Data” table, ContactsContract.data, is a schema independent table.
Design content URI
The content URI is the URI used to identify data in the provider. The content URI includes the symbolic name of the entire provider (its authorization) and a name that points to a table or file (path). The optional ID section points to a single row in the table. Each data access method of the ContentProvider takes the content URI as an argument; You can use this to determine which tables, rows, or files to access.
Design of authorization
Providers typically have a single authorization that acts as their Android internal name. To avoid conflicts with other providers, you should use Internet domain ownership (reversed) as the basis for provider authorization. Because this recommendation also applies to Android package names, you can define provider authorization as an extension of the package name that contains the provider. For example, if your Android package name is com.example., provide com.example.. The provider.
Design path structure
Developers typically create content URIs based on permissions by appending paths to individual tables. For example, if you have two tables: table1 and table2, you can generate the content URI com.example.. by combining the permissions in the previous example. The provider/table1 and com. Example.. The provider/table2. Paths are not limited to a single segment, and there is no need to create a table for each level of path.
Handle content URI IDS
By convention, a provider provides access to a single row in a table by accepting a content URI with the corresponding ID value at the end of the row. Also by convention, the provider matches this ID value with the _ID column of the table and performs requested access on the matching row.
This convention facilitates common design patterns for accessing provider applications. The application performs a query against the provider and uses the CursorAdapter to display the generated Cursor as a ListView. A CursorAdapter is defined if one of the columns in the Cursor must be an _ID
The user then selects one of the rows displayed on the UI to view or modify the data. The application gets the row from a ListView-enabled Cursor, gets the _ID of the row, appends it to the content URI, and then sends an access request to the provider. The provider can then perform queries or modifications on a particular row selected by the user.
Content URI pattern
To help you choose the action to perform on the incoming content URI, the provider API adds the utility class UriMatcher, which maps the content URI “schema” to integer values. You can use these integer values in a switch statement to select the desired action for one or more content URIs that match a particular pattern.
The content URI pattern matches the content URI using wildcards:
* : Matches any valid character string of any length
# : Matches a string of numeric characters of any length
As an example of designing and coding content URI handling, suppose a provider with the authorization com.example.app.provider recognizes the following content URIs pointing to a table:
Content: / / com. Example. App. The provider/table1: a table named the content: / / com. The example. The app. The provider/table2 / dataset1: A table named dataset1 content: / / com. Example. App. The provider/table2 / dataset2: A table named dataset2 content: / / com. Example. App. The provider/table3: A table named table3 provider can also identify additional row ids content URI, for example, the content: / / com. Example. App. Corresponding by the provider/table3/1 1 identifies the content URI in table3.
The following content URI schema can be used:
The content: / / com. Example. App. The provider / * matches any content URI of the provider. The content: / / com. Example. App. The provider/table2 / * : matching table dataset1 and table dataset2 content URI, but does not match the table1 or table3 content URI. The content: / / com. Example. App. The provider/table3 / # : Matching table3 single lines in URI, such as the content: / / com. Example. App. The provider/table3/6 corresponding to the lines from 6 logo URI. The following code snippet demonstrates how methods in UriMatcher work. This code handles the URI of the entire table differently from the URI of the single row. The content URI schema it uses for the table is content://
/
/
The method addURI() maps the authorization and path to an integer value. The match() method returns the integer value of the URI. The switch statement chooses between querying the entire table and querying a single record:
public class ExampleProvider extends ContentProvider {
...
// Creates a UriMatcher object.
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
/*
* 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 returnedfor 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
}
Copy the code
Another class, ContentUris, provides utility methods for handling the ID part of the content URI. The Uri and URi. Builder classes include utility methods for parsing existing Uri objects and building new ones.
Implement ContentProvider class
ContentProvider instances manage access to structured data sets by handling requests from other applications. All forms of access end up calling the ContentResolver, which in turn calls the concrete methods of the ContentProvider to obtain access.
Required method
The abstract class ContentProvider defines six abstract methods that you must implement as part of your concrete subclasses. All of these methods (except onCreate()) are called by a client application trying to access your content provider:
Query () retrieves data from your provider. Use parameters to select the table to query, the rows and columns to return, and the sort order of the results. Return the data as a Cursor object. Insert () inserts a new line in your provider. Use parameters to select the target table and get the column values to be used. Returns the content URI of the newly inserted row. Update () updates existing rows in your provider. Use arguments to select the tables and rows to update and get the updated column values. Returns the number of updated rows. Delete () removes rows from your provider. Use arguments to select tables and rows to delete. Returns the number of deleted rows. GetType () returns the MIME type corresponding to the content URI. This method is described in more detail in the MIME types section of the implementation content provider. OnCreate () initializes your provider. The Android system calls this method immediately after you create your provider. Note that the system only creates the ContentResolver object when it tries to access your provider.Copy the code
These methods have the same signature as the ContentResolver method of the same name.
All of these methods (except onCreate()) can be called by multiple threads at the same time, so they must be thread-safe.
Avoid running time operations in onCreate(). Defer the initialization task until needed.
Although you must implement these methods, your code simply returns the requested data type and does not need to do anything else. For example, you might want to prevent other applications from inserting data into certain tables. To do this, you can ignore the insert() call and return 0.
Implement the query() method
The contentProvider.query () method must return a Cursor object. If it fails, an Exception is raised. If you use the SQLite database as the data store, you 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 only return NULL if an internal error occurred during the query.
If you are not using an SQLite database as a data store, use one of the concrete subclasses of Cursor. For example, in the cursor implementation of the MatrixCursor class, each row is an Object array. For this class, use addRow() to add a new line.
Keep in mind that the Android system must be able to propagate Exceptions across process boundaries. Android can do this for the following exceptions that may help handle query errors:
IllegalArgumentException (you can choose to throw this exception when the provider receives an invalid content URI) NullPointerException
Implement the insert() method
The insert() method adds a new row to the corresponding table using the values in the ContentValues argument. If the column name is not included in the ContentValues parameter, you may want to provide its default value in your provider code or database schema.
This method should return the content URI of the new line. To build this method, append a new row’s _ID (or other primary key) value to the table’s content URI using withAppendedId().
Implement the delete() method
The delete() method does not need to actually delete rows from your data store. If you are using a synchronous adapter with a provider, you should consider adding a “remove” flag for deleted rows rather than removing rows entirely. The synchronization adapter can check for deleted rows, remove them from the server, and then remove them from the provider.
Implement the update() method
The update() method takes the same ContentValues argument as insert(), And the same selection and selectionArgs arguments used by delete() and contentProvider.query ().
Implement the onCreate() method
The Android system calls onCreate() when it launches the provider. You should only perform fast-running initialization tasks in this method and defer database creation and data loading until the provider actually receives the data request. If you run a long task in onCreate(), it slows down the startup of the provider, which in turn slows down the provider’s responsiveness to other applications.
For example, if you use an SQLite database, you can create a new SQLiteOpenHelper object in contentProvider.onCreate () and then create SQL tables when you first open the database. To simplify the process, for the first time in your calls getWritableDatabase (), it will automatically call SQLiteOpenHelper. The onCreate () method.
The following two code snippets show the ContentProvider. OnCreate () and SQLiteOpenHelper. The onCreate () the interaction between the. The first code snippet is an implementation of 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(); }}Copy the code
Under a code snippet is SQLiteOpenHelper. The onCreate () implementation, including a helper class:
. // A string that defines the SQL statementfor 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); }}Copy the code
Implement the content provider MIME type
The ContentProvider class has two methods that return a MIME type:
getType()
One of the required methods that you must implement for any provider.
getStreamTypes()
The method that the system requires to be implemented when your provider provides the file.
MIME type of the table
The getType() method returns a String in MIME format that describes the data type returned by the content URI parameter. Uri parameters can be schemas rather than specific URIs; In this case, you should return the data type associated with the content URI that matches the pattern.
For common data types such as text, HTML, or JPEG, getType() should return a standard MIME type for that data.
For content URIs that refer to one or more table rows, getType() should return the MIME type in an Android vendor-specific MIME format:
Type part: VND
Subtype parts:
- If the URI pattern is used for a single row: Android.cursor.item /
- If the URI pattern is used for multiple rows: android.cursor.dir/
Provider-specific parts: VND.
.
You provide
and
. The
value should be globally unique, and the
value should be unique in the corresponding URI schema. It is appropriate to select the name of your company or some part of the Android package name of your application as
. It is appropriate to select the identity string of the URI-associated table as
.
For example, if the provider’s authorization is com.example.app.provider and it exposes a table named table1, then the MIME types of multiple rows in Table1 are:
vnd.android.cursor.dir/vnd.com.example.provider.table1
Copy the code
For a single row in Table1, the MIME type is:
vnd.android.cursor.item/vnd.com.example.provider.table1
Copy the code
The MIME type of the file
If your provider provides files, implement getStreamTypes(). This method returns an array of MIME type Strings for the files that your provider can return for the given content URI. You should filter the MIME types you provide through the MIME type filter parameter so that only those MIME types that the client wants to process are returned.
For example, suppose the provider serves a photo image as a file in.jpg,.png, and.gif formats. If the application calls the ContentResolver. GetStreamTypes () is used when the filter string image / * (any is the content of the “image”), the ContentProvider. GetStreamTypes () method should return an array:
{ "image/jpeg"."image/png"."image/gif"}
Copy the code
If applied only to the JPG files are interested in, you can in the calling ContentResolver. GetStreamTypes () is used when the filter string * / jpeg, ContentProvider. GetStreamTypes () should return:
{"image/jpeg"}
Copy the code
If your provider does not provide any MIME types requested in the filter string, getStreamTypes() should return NULL.
Implementation protocol class
A protocol class is a public final class that contains constant definitions of URIs, column names, MIME types, and other provider-related metadata. This class ensures that the provider can be correctly accessed even if the actual value of the URI, column names, and so on changes, thereby establishing a contract between the provider and other applications.
Implement content provider permissions
By default, the data files stored on the internal storage of the device are private data files for your application and provider; The SQLiteDatabase database you create is private to your application and provider; By default, the data files you save to external storage are public and globally readable data files. You cannot use content providers to restrict access to files in external storage because other applications can use other API calls to read and write to them; Method calls to open or create files or SQLite databases on your device’s internal storage may grant both read and write access to all other applications. If you use an internal file or database as a provider’s storage area and grant it globally readable or globally writable access, the permissions you set for the provider in the manifest file do not protect your data. The default access to files and databases in internal storage is “private,” and you should not change this permission for the provider’s storage. If you want to use content provider permissions to control access to data, you should store the data in internal files, SQLite databases, or “the cloud” (for example, on remote servers), and you should keep the files and databases private to your application.
Implement permissions
Even if the underlying data is private, all applications can still read from or write to your provider, because by default, your provider has no permissions set. To change this, use properties or children of the
You can define permissions for your provider through one or more < Permission > elements in the manifest file. To make permissions unique to your provider, use Java style scopes for the Android: Name attribute. For example, will read permissions named 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 gradually refining them. More detailed permissions take precedence over more scoped permissions:
Unified read and write provider level permissions
A permission that controls both read and write access to the entire provider, specified through the Android: Permission attribute of the
Separate read and write provider level permissions
Read and write permissions for the entire provider. You can specify them through the Android :readPermission and Android :writePermission properties of the
Path level Permission
Read, write, or read/write permissions for the content URI in the provider. You can specify each URI that you want to control through the
Temporary permissions
A level of permission that grants temporary access to an application even if the application does not have the permissions normally required. Temporary access reduces the number of permissions an application needs to request in its manifest file. When you enable temporary access, only applications that continuously access all of your data require “permanent” provider access. Assuming you need to implement permissions for E-mail providers and applications, if you want to allow an external image viewer application to display photo attachments from your provider, you can set temporary permissions for the photo’s content URI in order to provide the image viewer with the necessary access without requesting permission. Design your E-mail application so that when the user wants to display a photo, the application sends an Intent to the image viewer that contains the photo’s content URI and a permission flag. The image viewer can then query your E-mail provider to retrieve photos, even if the viewer does not have normal read permissions on your provider.
If you want to enable temporary permissions, please set the < provider > element android: grantUriPermissions attribute, or to your < provider > element to add one or more < grant – uri – permission > child element. If you use the temporary access, each time you removed from the provider’s support for a content URI, and the content URI associated temporary permissions, all you need to call the Context. RevokeUriPermission ().
The value of this attribute determines the range of providers accessible. If this property is set to true, temporary permissions are granted to the entire provider, which supersede any other permissions required by your provider – or path-level permissions.
If this flag is set to false, you must add a
child element to the
To grant temporary access to an application, the Intent must contain the FLAG_GRANT_READ_URI_PERMISSION and/or the FLAG_GRANT_WRITE_URI_PERMISSION flag. They are set by the setFlags() method.
If the android: grantUriPermissions attribute doesn’t exist, assuming that it is false.
The element
Like Activity and Service components, you must subclass the ContentProvider for your application in the manifest file using the < Provider > element. The Android system gets the following information from this element:
Authorization (Android: Authorities) Symbolic name used to identify the entire provider within the system.
Provider class name (Android: Name) The class that implements ContentProvider.
permissions
Specify other applications to access data provider must have permission attributes: android: grantUriPermssions: temporary permission to sign android: permission: scope of unified provider read/write access android: readPermission: Provider scope read permission android:writePermission: provider scope writePermission
Start and control properties
These properties determine how and when the Android system starts the provider, the process characteristics of the provider, and other runtime Settings: Android: Enabled: flag that allows the system to start the provider. Android: Exported: a flag that allows other applications to use this provider. Android :initOrder: The startup order of this provider relative to other providers in the same process. Android :multiProcess: flag that allows 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 will be synchronized with data on the server.
Information property
Optional ICONS and labels for providers:
Android :icon: drawable object resource that contains provider ICONS. This figure marks the provider TAB in the app list now in Settings > Apps > All.
Android: Label: Information label that describes the provider and/or its data. This TAB appears in the app list in Settings > Apps > All.
Intent and data access
Applications can access content providers indirectly through intEnts. The application does not invoke any of the ContentResolver or ContentProvider methods, which are not provided directly, but instead send an Intent that launches an Activity that is usually part of the provider’s own application. The target Activity is responsible for retrieving and displaying data in its UI. Depending on the action in the Intent, the target Activity may also prompt the user to make changes to the provider’s data. The Intent may also contain “extra” data for the target Activity to display in the UI. The user can then choose to change this data and then use it to modify the data in the provider.