l 原理
l 从ContentProvider获取数据
l 插入、更新、删除数据
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。
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提供如下数据类型:
text
integer
long integer(long)
floating point(float)
long floating point(double)
Binary Large Object(BLOB)