Content Provider 负责管理访问结构化数据集,encapsulate 数据,提供安全访问数据机制。
访问数据的Client 与 提供数据的Provider 处于两个进程。
Client 使用ContentResolver, 负责与实现ContentProvider接口的实例进行进程间通信IPC。
一般来讲,无需开发provider,除非需要与其他应用共享数据,如提供自定义的搜索建议,复制或者粘贴复杂的数据或者文件到其他应用。
ContntResolver.query()会调用ContentProvider.query()获取数据。
// 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
其中,
query args | select keyword/parameters | 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 Cursor. |
ContentURI 标示一个数据Provider。由sybolic name(authority) 和 table name(path)组成。
content://user_dictionary/words
其中content://是scheme, user_dictionary 是authority, words是table.
许多provider允许访问单行数据,附上一个_ID。
Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4);
通常是在获取一系列数据后,然后想要更新某条数据或者删除某条数据。
For the sake of clarity, 为清晰起见,演示的代码都在UI线程中。但在实际的代码中,一般都在子线程中采用异步查询等操作。也可以使用CursorLoader。
从provider中获取数据,一般有两步:
1. 请求read access permission
2. 编写代码发送查询请求。
不可在运行时请求,只能在Manifest中静态指定, 和 exact permission name defined by provider.
当用户安装应用时,用户就会 implicitly grant(隐式授权)这个权限请求。
如用户词典User Dictionary Provider在其Manifest中定义了android.permission.READ_USER_DICTIONARY 权限。
构造请求查询代码
// 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 = {""};
mSelectionClause 是查询条件
而mSelectionArgs则是查询条件的参数,比如下面的代码。
/*
* 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
}
等化为SQL语句:
SELECT | _ID, word, locale | FROM words | WHERE word = | ORDER BY word ASC; | |
---|---|---|---|---|---|
.query() | projection | content_uri | selection | selectionArgs | orderby |
避免注入,推荐在selection使用?来代替。
// Constructs a selection clause with a replaceable parameter
String mSelectionClause = "var = ?";
// Sets the selection argument to the user's input
selectionArgs[0] = mUserInput;
CAUTION:若要使用Cursor备份一个ListView,该Cursor必须包含_ID列,即使ListView并没有显示_ID列。
To back a ListView with a Cursor, the cursor must contain a column named _ID.
从Cursor中获取数据
newWord = mCursor.getString(index);
或者是其他的getInteger等等类型。
指定权限以便于控制数据的访问。
另外,若是一个Provider什么权限都不指定,则其它应用将不可访问,但是该Provider所在的应用具有完全的访问读写能力。
读使能权限
android.permission.READ_USER_DICTIONARY
插入删除更新使能权限
android.permission.WRITE_USER_DICTIONARY
插入数据,调用ContentResolver.insert()
使用ContentValues 封装KV值,调用插入方法即可。
// 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
);
更新
需要query的selectionClause, selectionArgs 和 insert的content values.
// 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 );
删除
需要SelectionClause, selectionArgs, 方法返回被删除的行数。
// 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 );
Provide也同样支持MIME类型,支持使用MIME指定一个类的格式,
MIME data type information for each content URI they define.
批访问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: 使用CursorLoader进行异步查询。
Data access via intents: 通过Intents访问数据,使用Intent发送请求到Provider所在Application,然后Application解析intent,返回相应的数据。best-equipped to modify the provider’s data.
Batch Access
create ContentProviderOperation 类的数组,然后调用 ContentResolver.applyBatch()。只需要传递authority,不需要特定的path.
这允许数组中的每个ContentProviderOperation对象都能够工作在各自的table,并且Content.applyBatch()将返回一个结果数组。
This allows each ContentProviderOperation object in the array to work against a different table. A call to ContentResolver.applyBatch() returns an array of results.
Asynchrounous queries
使用方法如CursorLoader中的Example.
Data Access via intents
临时权限访问和写入
Read permission: FLAG_GRANT_READ_URI_PERMISSION
Write permission: FLAG_GRANT_WRITE_URI_PERMISSION
Provider在< provider>元素下定义android:grantUriPermission属性,以及 < grant-uri-permission> 子元素。
使用另一个具有权限的应用
例如:Calendar应用允许其他应用发送ACTION_INSERT,启动Calendar的插入UI.
the Calendar application accepts an ACTION_INSERT intent, which allows you to activate the application’s insert UI.
设定变量帮助应用与ContentProvider的content URIs, column names, intent actions, and other features 更好的交互。一般在android.provider下。
如
UserDictionary.Words.CONTENT_URI
用法 type/subtype, 常见的有text/html
自定义的MIME类型,vendor-specific 具有更复杂的类型和子类型。
多行
vnd.android.cursor.dir
单行
vnd.android.cursor.item
再比如:子类型的值
vnd.android.cursor.item/phone_v2