阅读:http://developer.android.com/guide/topics/providers/content-providers.html
When you want to access data in a content provider, you use the
object in your application's
to communicate with the provider as a client. The
object communicates with the provider object, an instance of a class that implements
. The provider object receives data requests from clients, performs the requested action, and returns the results.
当我们想获得content provider,可以使用 ContentResolver对象来获得provider object ,继承ContentProvider的一个对象。provider object能够从用户获得请求并返回结果。
// Queries the user dictionary and returns resultsmCursor = 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
一般通过ContentResolver的方法来进行请求,相应的参数如下:
query() argument SELECT keyword/parameter Notes 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 BY col,col,...
sortOrder
specifies the order in which rows appear in the returned.
content URI是一个在provide里已经定义好的URI,它是用户请求的一个URI。形式像是这样:
content://user_dictionary/words
你还可以通过URI直接连接到_ID
为某个值的那行,使用这个方法:
Uri singleUri =ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4);
最后的参数为
的值。_ID
官方文档中,建议使用异步操作进行数据的请求:
一般数据的请求还要有相关的权限。
/* * This defines a one-element String array to contain the selection argument. */String[] mSelectionArgs = {""};// Gets a word from the UImSearchString = mSearchWord.getText().toString();// Remember to insert code here to check for invalid or malicious input.// If the word is the empty string, gets everythingif (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 objectmCursor = 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 exceptionif (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}
以上就是一个例子,注意,之所以用“ = ? ” 以及后面的参数集合来查询,是为了避免用户非法操作。
查询会返回结果,如果是NULL则表示出错,而查询正确则会返回Cursor,如果查询结果为0,那么Cursor.getCount()==0;
// Defines a list of columns to retrieve from the Cursor and load into an output rowString[] 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 rowint[] mWordListItems = { R.id.dictWord, R.id.locale};// Creates a new SimpleCursorAdaptermCursorAdapter = 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 ListViewmWordList.setAdapter(mCursorAdapter);
上面例子表示了如何和适配器搭配使用。
Note: To back a
with a
, 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 thedoesn't display it. This restriction also explains why most providers have a
_ID
column for each of their tables.
// 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.}
// Defines a new Uri object that receives the result of the insertionUri mNewUri;...// Defines an object to contain the new values to insertContentValues 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);
通过新建一个ContentValues来进行插入数据的存储,然后后面直接用insert插入,注意_ID是自动插入的不需要自己添加,返回的uri可以通过ContentUris.parseId().获取_ID。
// Defines an object to contain the updated valuesContentValues mUpdateValues = new ContentValues();// Defines selection criteria for the rows you want to updateString mSelectionClause = UserDictionary.Words.LOCALE + "LIKE ?";String[] mSelectionArgs = {"en_%"};// Defines a variable to contain the number of updated rowsint 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);
插入代码如上。
// Defines selection criteria for the rows you want to deleteString mSelectionClause = UserDictionary.Words.APP_ID + " LIKE ?";String[] mSelectionArgs = {"user"};// Defines a variable to contain the number of rows deletedint mRowsDeleted = 0;...// Deletes the words that match the selection criteriamRowsDeleted = getContentResolver().delete( UserDictionary.Words.CONTENT_URI, // the user dictionary content URI mSelectionClause // the column to select on mSelectionArgs // the value to compare to);
删除的代码。
接下来谈谈Content-provider如何设计开发
首先,基于这样的需求你才需要CP:
1、与其他的APP有一定数据的交互;
2、需要用到搜索那块;
设计流程:
1、考虑数据形式:如果是文件,建议存放在私有空间,然后通过CP获得手柄去访问,又或者,只是单纯的数据结构,那么就可以通过类似数据库等来解决。
2、实现CP的类;
3、Define the provider's authority string, its content URIs, and column names.
此外,还需要考虑:主键的问题;对于文件数据还是存为文件然后让CP提供访问较好;
接下来,你还要设计名字,权限,结构等问题比较主要的是ID问题。
接下来就是,URI的配对问题了。
To help you choose which action to take for an incoming content URI, the provider API includes the convenience class
, which maps content URI "patterns" to integer values. You can use the integer values in a
switch
statement that chooses the desired action for the content URI or URIs that match a particular pattern.A content URI pattern matches content URIs using wildcard characters:
*
: Matches a string of any valid characters of any length.#
: Matches a string of numeric characters of any length.As an example of designing and coding content URI handling, consider a provider with the authority
com.example.app.provider
that recognizes the following content URIs pointing to tables:
content://com.example.app.provider/table1
: A table calledtable1
.content://com.example.app.provider/table2/dataset1
: A table calleddataset1
.content://com.example.app.provider/table2/dataset2
: A table calleddataset2
.content://com.example.app.provider/table3
: A table calledtable3
.The provider also recognizes these content URIs if they have a row ID appended to them, as for example
content://com.example.app.provider/table3/1
for the row identified by1
intable3
.The following content URI patterns would be possible:
content://com.example.app.provider/*
- Matches any content URI in the provider.
content://com.example.app.provider/table2/*
:- Matches a content URI for the tables
dataset1
anddataset2
, but doesn't match content URIs fortable1
ortable3
.content://com.example.app.provider/table3/#
: Matches a content URI for single rows intable3
, such ascontent://com.example.app.provider/table3/6
for the row identified by6
.The following code snippet shows how the methods in
work. This code handles URIs for an entire table differently from URIs for a single row, by using the content URI pattern
content://<authority>/<path>
for tables, andcontent://<authority>/<path>/<id>
for single rows.The method
maps an authority and path to an integer value. The method
returns the integer value for a URI. A
switch
statement chooses between querying the entire table, and querying for a single record:
public class ExampleProvider extends ContentProvider {... // Creates a UriMatcher object. private static final UriMatcher sUriMatcher;... /* * The calls to addURI() go here, for all of the content URI patterns that the provider * should recognize. For this snippet, only the calls for table 3 are shown. */... /* * Sets the integer value for multiple rows in table 3 to 1. Notice that no wildcard is used * in the path */ sUriMatcher.addURI("com.example.app.provider", "table3", 1); /* * Sets the code for a single row to 2. In this case, the "#" wildcard is * used. "content://com.example.app.provider/table3/3" matches, but * "content://com.example.app.provider/table3 doesn't. */ sUriMatcher.addURI("com.example.app.provider", "table3/#", 2);... // Implements ContentProvider.query() public Cursor query( Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {... /* * Choose the table to query and a sort order based on the code returned for the incoming * URI. Here, too, only the statements for table 3 are shown. */ switch (sUriMatcher.match(uri)) { // If the incoming URI was for all of table3 case 1: if (TextUtils.isEmpty(sortOrder)) sortOrder = "_ID ASC"; break; // If the incoming URI was for a single row case 2: /* * Because this URI was for a single row, the _ID value part is * present. Get the last path segment from the URI; this is the _ID value. * Then, append the value to the WHERE clause for the query */ selection = selection + "_ID = " uri.getLastPathSegment(); break; default: ... // If the URI is not recognized, you should do some error handling here. } // call the code to actually do the query }
然后,继承CP需要重写一些方法:
query()
根据所提出的要求返回Cursor。Cursor有可能需要自己去实现
insert()插入新数据,返回新数据的URI
update()更新数据,返回更新的数目。delete()删除,返回删除数目。getType() Return the MIME type corresponding to a content URI. This method is described in more detail in the section Implementing Content Provider MIME Types. onCreate()当ContentResolver使用的时候就会启动这个方法,初始化CP吧!
Your implementation of these methods should account for the following:
- All of these methods except
can be called by multiple threads at once, so they must be thread-safe. To learn more about multiple threads, see the topic .
- Avoid doing lengthy operations in
. Defer initialization tasks until they are actually needed. The section discusses this in more detail.
- Although you must implement these methods, your code does not have to do anything except return the expected data type. For example, you may want to prevent other applications from inserting data into some tables. To do this, you can ignore the call to
and return 0.
除了oncreate之外,其他方法都要加上线程安全。
不要再oncreate执行太多操作。
并不是每个方法都要很认真的返回所要求的数据,根据需求看吧!
接下来就是关于CP的权限了,是在manifest立马配置的,而且,有多种类型:
- Single read-write provider-level permission
- 一次性定义读和写权限; Separate read and write provider-level permission
- 分开定义android:readPermission and android:writePermission。 Path-level permission
- 定义特定路径下的路径,详细见官方。 Temporary permission 可赋予其他APP临时权限。
Like
and
components, a subclass of
must be defined in the manifest file for its application, using the
element. The Android system gets the following information from the element:
Authority ( )
- Symbolic names that identify the entire provider within the system. This attribute is described in more detail in the section .
Provider class name ()
- The class that implements
Permissions. This class is described in more detail in the section .
- Attributes that specify the permissions that other applications must have in order to access the provider's data:
Startup and control attributes
: Temporary permission flag.
: Single provider-wide read/write permission.
: Provider-wide read permission.
: Provider-wide write permission.
Permissions and their corresponding attributes are described in more detail in the section .
- These attributes determine how and when the Android system starts the provider, the process characteristics of the provider, and other run-time settings:
Informational attributes
: Flag allowing the system to start the provider.
: Flag allowing other applications to use this provider.
: The order in which this provider should be started, relative to other providers in the same process.
: Flag allowing the system to start the provider in the same process as the calling client.
: The name of the process in which the provider should run.
: Flag indicating that the provider's data is to be sync'ed with data on a server.
The attributes are fully documented in the dev guide topic for the
element.
- An optional icon and label for the provider:
: A drawable resource containing an icon for the provider. The icon appears next to the provider's label in the list of apps in Settings > Apps > All.
: An informational label describing the provider or its data, or both. The label appears in the list of apps in Settings > Apps > All.
The attributes are fully documented in the dev guide topic for the
element.