Content Providers详解

今天仔细阅读了一遍Content Providers的官方API文档,总结了一下Android中Content Providers的用法。

 

各种类型的Content Provider对一个结构化的数据集的读写进行管理。它们封装数据,并提供保证数据安全的机制。Content providers是数据在正在运行的一个进程和另一个进程之间联系的标准接口。

当你想使用content provider读取数据时,你在程序的上下文中使用 ContentResolver 对象作为 provider 的 provider client,ContentResolverprovider的一个实例,provider接受provider client的数据请求,执行所请求的操作并返回结果。

如果你不需要和其它应用程序共享数据的话不用开发你自己的provider。但是,如果你想在你的应用程序中提供自定义的数据获取方式,或者在应用程序之间复制和粘贴复杂的数据或文件,你可以自己开发其它的provider。

Android自身包含各类content provider用来管理音频、视频、图片和联系人信息。你可以在android.provider这个包的参考文档中查询它们的详细用法。

 

数据被组织在一张表中,怎样通过Content Provider访问它们?

 

Content Provider 基础

Content Provider管理中央数据存储的访问和存取操作。Content Provider是安卓应用的一部分,提供自己处理数据的UI。但是,Content Provider还是主要被用于其它应用程序通过provider client类来使用provider。总之,providers 和 provider clients提供一个对数据一致、标准的接口,并处理进程间的通信和数据安全。

本文主要描述下面的基础知识:

1、content providers 如何工作

2、content provider提供数据检索的API

3、content provider提供数据插入、更新、删除的API

4、其它API功能

 

综述

content provider提供给外部应用程序的数据形式类似于关系型数据库的表。表中的每一行代表某个类型的一个实例,行中的每一列表示这个实例的一项数据。

例如,user dictionary是Android平台上的一种内建provider,用来存储用户想要保存的不是标准拼写的单词。表-1展示了provider表的形式:

使用provider

一个应用程序借助ContentResolver这个client object访问content provider中的数据。这个对象中的方法名和provider中的方法名相同,它是ContentProvider具体子类的一个实例,ContentProvider方法提供基本的“创建、检索、更新、删除”功能和持久的存储。

(英语水平有限,这段话是在不知道怎么表达才好,直接上原文吧)

The ContentResolver object in the client application's process and the ContentProvider object in the application that owns the provider automatically handle inter-process communication. ContentProvider also acts as an abstraction layer between its repository of data and the external appearance of data as tables.

例如,要向User Dictionary Provider获取一些单词及它们的本地环境数据,你可以调用ContentResolver.query()。query()方法会调用由User Dictionary Provider定义的 ContentProvider.query() 方法。下面代码展示了 ContentProvider.query()调用:

1 // Queries the user dictionary and returns results
2 mCursor = getContentResolver().query(
3     UserDictionary.Words.CONTENT_URI,   // The content URI of the words table
4     mProjection,                        // The columns to return for each row
5     mSelectionClause                    // Selection criteria
6     mSelectionArgs,                     // Selection criteria
7     mSortOrder);                        // The sort order for the returned rows

表-2展示了 query(Uri,projection,selection,selectionArgs,sortOrder)中各参数对应的SQL SELECT 语句:

Content URIs

 

 Content URI是Provider中用来识别数据的URI(统一资源标识符)。 Content URIs包括整个Provider的符号名(它的身份认证)和一个指向表的名字(一个路径)。当你调用一个client method访问Provider中的表时,表的content URI是其中的一个参数。

 在上面的几行代码中,CONTENT_URI包含了user dictionary的单词表的content URI。ContentResolver对象解析URI的身份认证,用这个身份认证和系统中已知的provider列表比较,ContentResolver把正确的查询请求参数传给对应的Provider。

ContentProvider使用Content URI中的部分路径选择要访问的表。一个Provider通常拥有所有可用表的路径。

在前面的代码中,单词表完整的URI路径是:

content://user_dictionary/words
user_dictionary是provider的身份认证, words是表的路径,content://表示这是一个Content URI。
许多Provider允许你通过在URI结尾附加ID值访问表中的某一行。例如:检索user dictionary中_ID值为4的一行,你可以使用这个Content URI:
Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4);

 检索Provider中的数据

 

检索Provider中的数据有以下两个基本的步骤:

1、获得Provider的访问权限

2、向Provider发送请求

 

获取权限

访问权限必须在manifest中使用<uses-permission>加上Provider的权限名来获取。Provider详细的访问权限名在Provider的文档中。

例如:User Dictionary Provider的访问权限在mainifest文件中定义为android.permission.READ_USER_DICTIONARY,所以应用程序想访问这个Provider时,就必须获得这个权限。

构造请求

第二步就是构造要发送给Provider的请求了。下面第一段代码是访问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 = {""};

接下来,下面将要看到的第二段代码以User Dictionary Provider为例展示了如何使用ContentResolver.query()。一个provider client请求相当于一条SQL语句的请求,它包含一组选择和排序的标准,返回值是表中的一组列。

这些返回的列被称为“projection(the variable mProjection)”

对检索行进行指定的表示式可分为“选择字句”和“选择参数”,“选择字句”是一些逻辑和布尔表达式、列名、一些值(mSelectionClause)的组合。如果你指定用参数?来替代一个值,查询方法从选择参数数组(mSelectionArgs)中检索值。

在第二段代码中,如果你不输入单词,“选择字句”是空的,查询操作返回Provider中的所有单词。如果你输入单词,“选择字句”被设置为UserDictionary.Words.WORD + " = ?"并且选择数组(mSelectionArgs)中的第一个元素被设置为用户所输入的单词。

/*
 * 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

}

这条查询类似于SQL语句:

SELECT _ID, word, locale FROM words WHERE word = <userinput> ORDER BY word ASC;

防止恶意输入

查询结果的展示

ContentResolver.query()的client方法总是返回一个Cursor,它包含着满足查询条件的行。一个Cursor对象提供对行和列的随机访问,使用cursor可以遍历结果集中的每一行,

 

 













posted @ 2014-08-14 15:37  DF的翱翔  阅读(733)  评论(0编辑  收藏  举报