目的
l 原理
l 從ContentProvider獲取數據
l 插入、更新、刪除數據
Content Provider 工作原理
l 概覽
Content Provider對外部應用程序提供數據共享的表示形式,和關係數據庫的表結構類似,例如下面用戶字典的表格。每一行唯一表示了一個單詞的信息。
word |
app id |
frequency |
locale |
_ID |
mapreduce |
user1 |
100 |
en_US |
1 |
precompiler |
user14 |
200 |
fr_FR |
2 |
applet |
user2 |
225 |
fr_CA |
3 |
const |
user1 |
255 |
pt_BR |
4 |
int |
user5 |
100 |
en_UK |
5 |
l 訪問Provider
應用程序中需要訪問某個Content Provider的數據是通過ContentResolver對象實現的。Content Provider對象實例的方法提供了基本的CRUD方法用於數據的持久化操作。另外,爲達到這些操作的目的,需要在manifast file中提供相應的權限:Content Provider Permissions.
例如,要查詢Provider所有的用戶單詞和locales,需要調用ContentResolver.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
l Content URIs
一個content URI 唯一標識了provider的數據資源。每個Content URL包括了Provider的入口和數據表格的路徑(表格的某列)。當我們要去訪問Provider數據資源時,也就是在調用ContentResolver的方法,URI將會作爲一個參數傳入。例如,訪問字典中的單詞時,URI描述如下:
content://user_dictionary/words
通常,我們希望獲取某指定行的數據,需要給URI追加指定行的ID,例如,獲取_ID爲4的行:
Uri singleUri = ContentUri.withAppendedId(UserDictionary.Words.CONTENT_URI,4);//withAppendedId用於對URI追加指定的ID。
獲取provider的數據
l 查詢的構建
1.便於後面的處理,下面代碼片段是用於訪問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 = {""};
2下一個片段中是介紹ContentResolver.query()的詳細使用。對於provider的查詢實際上與SQL有着相同的語法。如下是對word的條件查詢:
/*
* 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
}
上面的功能與下面的查詢語句是相似的:
SELECT _ID, word, frequency, locale FROM words WHERE word = <userinput> ORDER BY word ASC;
l 防止惡意攻擊和SQL注入
在如下的代碼片段中:
// Constructs a selection clause by concatenating the user's input to the column name
String mSelectionClause = "var = " + mUserInput;
如果用戶輸入nothing; DROP TABLE *,那麼產生的結果是數據庫的表將會全部被清除。
爲了避免這種結果,可以用“?”代替,如:
// Constructs a selection clause with a replaceable parameter
String mSelectionClause = "var = ?";
l 數據集的展示
ContentResolver.query()查詢返回一個Cursor實例,它提供按行迭代訪問數據的方法。
如果沒有匹配的查詢結果,返回的Cursor掉用getCount()返回0;
如果出現異常,返回null.
我們知道,既然Cursor是一個按行的記錄列(list of rows),比較好的展現數據的形式是通過SimpleCursorAdapter綁定數據的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);
l 數據集中數據的遍歷
// 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提供許多“get”方法來獲取不同類型的數據,如上面的getString().當然,getType()方法可以獲取某一列的數據類型。
插入、更新和刪除數據
l 插入數據
使用ContentResolver.insert()方法來插入一行數據,該方法返回這一行的URI,代碼片段如下:
// 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
);
若某一列的數據爲空,則可以賦值null或者使用ContentValues.putNull();我們注意到在插入數據的時候沒有給_ID賦值,這是因爲該列是自增的。在我們不需要_ID的時候完全可以不必要用(ListView 除外);如果需要或得該Uri的_ID,調用ContentUris.parseId();
l 更新數據
更新數據用到ContentResolver.update();示例如下:
// 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
);
l 刪除數據
刪除數據使用getContentResolver().delete.
// 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
);
Provider數據類型
Provider提供如下數據類型:
text
integer
long integer(long)
floating point(float)
long floating point(double)
Binary Large Object(BLOB)