今天仔细阅读了一遍Content Providers的官方API文档,总结了一下Android中Content Providers的用法。
各种类型的Content Provider对一个结构化的数据集的读写进行管理。它们封装数据,并提供保证数据安全的机制。Content providers是数据在正在运行的一个进程和另一个进程之间联系的标准接口。
当你想使用content provider读取数据时,你在程序的上下文中使用 ContentResolver 对象作为 provider 的 provider client,ContentResolver是provider的一个实例,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()调用:
// 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
表-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可以遍历结果集中的每一行,