5、访问Content Provider
这里首先学习如何使用Content provider(包括系统提供的,比如创建一个短信收发系统)。
Content Providers的用户都不可能直接访问到Content Provider实例,只能通过ContentResolver在中间代理。客户端直接使用Content Resolver对象进行交互,Content Resolvers 方法提供了基本的持续存储数据的函数CRUD(create、retrieve、update和delete)。客户端如何获取ContentResolver的实例,可通过如下Activity的成员方法获取。不过当你访问provider时,你的应用程序应在mainfest文件请求相应的权限。
ContentResolver cr= getContext().getContentResolver();
ContentResolver提供了5个基本的对Content Provider操作的方法,后面将会深入介绍。当你创建自己的Content Provider,也就是继承了ContentProvider类时,意味着你也同时实现这5个方法(在后续章节会介绍到)。
1、final Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
//Query the given URI, returning a Cursor over the result set.
final Cursor managedQuery(Uri uri,String[] projection,String selection,String[]selectionArgs,String sortOrder)
2、final Uri insert(Uri url, ContentValues values)
//Inserts a row into a table at the given URL.
3、final int update(Uri uri, ContentValues values, String where, String[] selectionArgs)
//Update row(s) in a content URI.
4、final int delete(Uri url, String where, String[] selectionArgs)
//Deletes row(s) specified by a content URI.
5、final String getType(Uri url)
//Return the MIME type of the given content URL.
上面面提供的两种查询方法,区别在于方法managedQuery()的Cursor的生命周期由activity管理。管理的好处:activity帮你处理细节,如activity暂停时卸载,activity重新开始时重新请求。对于未被activity管理的Cursor可通过此函数让activit管理:
Activity.startManagingCursor(Cursor cursor)
6、从provider查询数据
查询主要下面两步(以User Dictinary为例)
(1)请求读取权限。在mainfest文件中配置android.permission.READ_USER_DICTIONARY
(2)自定义查询的代码。这里主要有两种处理方式:一种是显示查询的结果,另外一种是获取查询的数据。
另外当你精确查找某一个记录时,也就意味着你的里Uri需要包含其_ID值,可以通过以下两种方法追加,如下:
Uri myPerson = ContentUris.withAppendedId(People.CONTENT_URI, 23);
Uri myPerson = Uri.withAppendedPath(People.CONTENT_URI, "23");
6.1 首先需要构造一个查询
// 指定要返回的每行的字段
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
};
// 定义查询子句的字符串(类似Sql的where子句,但无where关键字)
String mSelectionClause = null;
// 初始化数组,其包含查询子句的参数值(即选择字句占位符“?”值)
String[] mSelectionArgs = {""};
/*
* This defines a one-element String array to contain the selection argument.
*/
String[] mSelectionArgs = {""};
// 获取用户输入
mSearchString = mSearchWord.getText().toString();
//记得检查用户输入有效性
// 为空就用null,代表查询所有的数据
if (TextUtils.isEmpty(mSearchString)) {
mSelectionClause = null;
mSelectionArgs[0] = "";
} else {
// 根据用户输入构造查询子句
mSelectionClause = UserDictionary.Words.WORD + " = ?";
// 用户输入的字符
mSelectionArgs[0] = mSearchString;
}
// 通过ContextResolver对象查询,返回Cursor对象
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
// 对查询的cursor处理(空或者异常等)
if (null == mCursor) {
/*
* 处理错误,此时mCursor不能使用
* call android.util.Log.e() to log this error.
*
*/
// If the Cursor is empty, the provider found no matches
} else if (mCursor.getCount() < 1) {
/*
* 查询失败,通知用户无效查询子句
* 也有可能是你想推荐给用户的输入
*/
} else {
// 处理正确的查询结果
}
注:使用占位符“?”,可以有效阻止恶意的SQL语句,也就是为什么间接使用mSelectionClause和mSelectionArgs来构造where语句的原因。
6.2.1显示查询结果
查询返回的是Cursor,此对象类型是列表行,因此显示Cursor内容最好凭借SimpleCursorAdapter填充器将之链接到ListView上。
(1)如果不匹配selection,provider返回空的Cursor对象,此时Cursor.getCount()为0
(2)如果发生内部错误,查询结果取决于provider(可能返回null,也可能为Exception)
// 定义从Cursor返回的字段列String[] mWordListColumns =
{
UserDictionary.Words.WORD, // Contract class constant containing the word column name
UserDictionary.Words.LOCALE // Contract class constant containing the locale column name
};
// 定义返回字段将要填充的View的id,也就是int to[]
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);
6.2.2 从查询结果中获取数据
// 获取该字段的索引
int index = mCursor.getColumnIndex(UserDictionary.Words.WORD);
/*
* 如果cursor有效时才执行,可能返回null、内部错误或者Exception */
if (mCursor != null) {
/*
* 在执行while语句即移动前,应首先在这里将cursor移动到下一行
*如果尝试直接查询数据,将会获得异常, 因为"row pointer" 是 -1
*/
while (mCursor.moveToNext()) {
// 从字段索引中获取相应的值
newWord = mCursor.getString(index);
// 处理查询的值.
...
// end of while loop
}
} else {
//如果cursor为null或者provider扔出exception,处理
}
6.3插入数据:
使用ContentResolver.insert()方法,其插入新行到provider,同时返回新加行的URI
// 定义一个新的Uri对象,其接收插入的结果
Uri mNewUri;
...
// 定义一个对象用来暂存需插入的值
ContentValues mNewValues = new ContentValues();
/*
* 设置插入的每一个字段的值,参数:字段名,字段值
*/
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
);
注:(1)由于SQLite数据库特性,这里不需要放置与字段相同类型的数据,甚至你可以不需要指定值,使用ContentValues.putNull()。
(2)主键自动增长(autoincrement),provider分配给每一行独一的_ID,用作主键。
(3)返回的新的URI如下,<id_value>就是_ID值,可利用该id对此行执行相应操作。获取此_ID值,通过ContentUris.parseId();
content://user_dictionary/words/<id_value>
6.4更新数据
客户端使用ContentResolver.update()更新数据,返回更新的行数。利用ContentValues对象暂存数据,其中只需要存储想改变的字段。若想清除,直接设置值为null就可以了。
// 定义ContentValues对象,暂存需更新的数据
ContentValues mUpdateValues = new ContentValues();
// 定义想更新的行的查询子句
String mSelectionClause = UserDictionary.Words.LOCALE + "LIKE ?";
String[] mSelectionArgs = {"en_%"};
// 定义一变量,存储被更新的行的总数
int mRowsUpdated = 0;
...
/*
* 需更新值得存储
*/
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
);
6.5删除数据
类似于查询,指定想删除行的条件,同时将返回删除的行数。执行删除操作时,应当对用户的输入采取一定错数,以防恶意输入。
// 定义删除条件
String mSelectionClause = UserDictionary.Words.APP_ID + " LIKE ?";
String[] mSelectionArgs = {"user"};
// 定义变量,存储删除的行数
int mRowsDeleted = 0;
...
// 删除
mRowsDeleted = getContentResolver().delete(
UserDictionary.Words.CONTENT_URI, // the user dictionary content URI
mSelectionClause // the column to select on
mSelectionArgs // the value to compare to
);