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 );