Android内容提供程序

来源:互联网 发布:115 mac版 编辑:程序博客网 时间:2024/05/23 11:48

内容提供程序管理对结构化数据集的访问,它们封装数据,并供用于定义数据安全性的机制。内容提供程序是连接一个进程中的数据与另一个进程中运行的代码的标准界面。

将应用的Context中的ContentResolver对象用作客户端来提供程序通信,可以访问内容提供程序中的数据。ContentResolver对象会实现ContentProvider的类实例通信,提供程序对象从客户端接收数据请求,执行请求的操作并返回结果。

内容提供程序以一个或多个表的形式将数据呈现给外部应用,行表示提供程序收集的某种数据类型的实例,行中的每个列表示为实例收集的。

访问提供程序

应用从具有ContentResolver客户端对象的内容提供程序访问数据,此对象具有调用提供程序对象中同名方法的方法,ContentResolver方法可提供程序存储的基本CRUD(创建,检索,更新,删除)功能。客户端应用进程中ContentResolver对象和拥有提供程序的应用中的ContentProvider对象可自动处理跨进程通信,ContentProvider还可充当其数据存储区和表格外部显示之间的抽象层。

内容URI

是用于在提供程序中表示数据的URI,包括整个提供程序的符号名称,和一个指向表的名称(路径)。ContentResolver对象会分析出URI的授权,并通过将该授权与已知提供程序的系统进行比较来解析提供程序,然后,ContentResolver可以将查血的参数分派给正确的提供程序。

ContentProvider使用内容URI的路径部分来选择要访问的表,提供程序通常会为其公开每个表显示一条路径。

例如:content://user_dictionary/words

其中user_dictionary字符串是提供程序的授权,words字符串是表的路径,字符串content://(架构)始终显示,并将此标识为内容URI

许多提供程序都允许通过将ID值追加到URI末尾来访问表中的单个行。在检索到一组行后想要更新或删除其中某一行时通常用到ID值。

Uri和Uri.Builder 类包含根据字符串构建格式规范的URI对象的便利方法,ContentUris包含一些可以将ID值追加到URI后的方法。例如:

Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI, 4);

提供程序检索数据

为了说明,本例在UI线程上调用ContentResolver.query(),但在实际代码中应该在单独线程上异步执行查询,使用CursorLoader类,按照以下两个基本操作执行

1.请求对提供程序的读取访问权限

2.定义将查询发送至提供程序的代码

在清单文件<uses-permission>中添加读取访问权限

构建查询

// A "projection" defines the columns that will be returned for each rowString[] 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 clauseString mSelectionClause = null;// Initializes an array to contain selection argumentsString[] mSelectionArgs = {""};
查询应该返回的列集被称为投影(即变量mProjection)
String[] mSelectionArgs = {""};// Gets a word from the UImSearchString = mSearchWord.getText().toString();// Remember to insert code here to check for invalid or malicious input.// If the word is the empty string, gets everythingif (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 objectmCursor = 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 exceptionif (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}
如果用户未输入字词,则选择子句将设置为 null,而且查询会返回提供程序中的所有字词。 如果用户输入了字词,选择子句将设置为 UserDictionary.Words.WORD + " = ?" 且选择参数数组的第一个元素将设置为用户输入的字词。

防止恶意输入

如果内容提供程序管理的数据位于 SQL 数据库中,将不受信任的外部数据包括在原始 SQL 语句中可能会导致 SQL 注入。

String mSelectionClause =  "var = " + mUserInput;
如果执行此操作,则会允许用户将恶意SQL串连接到SQL语句上,由于选择子句是作为SQL语句处理,故可能会导致提供程序擦除基础SQLite数据库中的所有表。
要避免此问题,可使用一个用于将 ? 作为可替换参数的选择子句以及一个单独的选择参数数组。 执行此操作时,用户输入直接受查询约束,而不解释为 SQL 语句的一部分。 由于用户输入未作为 SQL 处理,因此无法注入恶意 SQL。

String mSelectionClause =  "var = ?";String[] selectionArgs = {""};selectionArgs[0] = mUserInput;

一个用于将 ? 用作可替换参数的选择子句和一个选择参数数组是指定选择的首选方式,即使提供程序并未基于 SQL 数据库。

显示查询结果
ContentResolver.query()客户端方法始终会返回符合以下条件的Cursor包含查询的投影为匹配查询选择条件的行指定的列.Cursor对象为其包含的行和列提供随机读取访问权限。 通过使用Cursor方法,您可以循环访问结果中的行、确定每个列的数据类型、从列中获取数据,并检查结果的其他属性。 某些Cursor实现会在提供程序的数据发生更改时自动更新对象和/或在Cursor更改时触发观察程序对象中的方法。

如果没有与选择条件匹配的行,则提供程序会返回Cursor.geeCount() 为 0(空游标)的Cursor 对象。

如果出现内部错误,查询结果将取决于具体的提供程序。它可能会选择返回 null,或引发Exception

由于Cursor是行“列表”,因此显示Cursor 内容的良好方式是通过SimpleCursorAdapter将其与ListView 关联。

内容提供程序权限

提供程序的应用可以指定其他应用访问提供程序的数据所必需的权限。 这些权限可确保用户了解应用将尝试访问的数据。 根据提供程序的要求,其他应用会请求它们访问提供程序所需的权限。 最终用户会在安装应用时看到所请求的权限。

如果提供程序的应用未指定任何权限,则其他应用将无权访问提供程序的数据。 但是,无论指定权限为何,提供程序的应用中的组件始终具有完整的读取和写入访问权限。

要获取访问提供程序所需的权限,应用将通过其清单文件中的<uses-permission>元素来请求这些权限。Android 软件包管理器安装应用时,用户必须批准该应用请求的所有权限。 如果用户批准所有权限,软件包管理器将继续安装;如果用户未批准这些权限,软件包管理器将中止安装。

插入、更新、删除数据

与从提供程序检索数据的方式相同,也可以通过提供程序客户端和提供程序ContentProvider之间的交互来修改数据。 您通过传递到ContentProvider的对应方法的参数来调用 ContentResolver方法。 提供程序和提供程序客户端会自动处理安全性和跨进程通信。

插入:
// Defines a new Uri object that receives the result of the insertionUri mNewUri;...// Defines an object to contain the new values to insertContentValues mNewValues = new ContentValues();/* * Sets the values of each column and inserts the word. The arguments to the "put" * method are "column name" and "value" */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);
更新:
// Defines an object to contain the updated valuesContentValues mUpdateValues = new ContentValues();// Defines selection criteria for the rows you want to updateString mSelectionClause = UserDictionary.Words.LOCALE +  "LIKE ?";String[] mSelectionArgs = {"en_%"};// Defines a variable to contain the number of updated rowsint mRowsUpdated = 0;.../* * Sets the updated value and updates the selected words. */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);
删除:
// Defines selection criteria for the rows you want to deleteString mSelectionClause = UserDictionary.Words.APP_ID + " LIKE ?";String[] mSelectionArgs = {"user"};// Defines a variable to contain the number of rows deletedint mRowsDeleted = 0;...// Deletes the words that match the selection criteriamRowsDeleted = getContentResolver().delete(    UserDictionary.Words.CONTENT_URI,   // the user dictionary content URI    mSelectionClause                    // the column to select on    mSelectionArgs                      // the value to compare to);
提供程序访问的替代形式
批量访问:可以通过ContentProviderOperation类中的方法创建一批访问调用,然后通过ContentResolver.applyBatch()应用它们。
异步查询:应该在单独线程中执行查询,使用CursorLoader对象。
通过Intent访问数据:尽管无法直接向提供程序发送Intent,但可以向提供程序的应用发送Intent,后者通常具有修改提供程序数据的最佳配置。
协定类
协定类定义帮助应用使用内容 URI、列名称、 Intent 操作以及内容提供程序的其他功能的常量。 协定类未自动包含在提供程序中;提供程序的开发者需要定义它们,然后使其可用于其他开发者。 Android 平台中包含的许多提供程序都在软件包android.provider中具有对应的协定类。

0 0
原创粉丝点击