android数据共享之Content Provider(访问篇CRUD)

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




你可能感兴趣的:(android,Provider,content)