【翻译】(9-补丁0-1)内容提供者基础
see
http://developer.android.com/guide/topics/providers/content-provider-basics.html
原文见
http://developer.android.com/guide/topics/providers/content-provider-basics.html
-------------------------------
Content Provider Basics
内容提供者基础
-------------------------------
In this document
本文目录
* Overview 概览
* Accessing a provider 访问一个提供者
* Content URIs 内容URI
* Retrieving Data from the Provider 从提供者中取出数据
* Requesting read access permission 请求读访问权限
* Constructing the query 构造查询
* Displaying query results 显示查询结果
* Getting data from query results 从查询结果中获取数据
* Content Provider Permissions 内容提供者权限
* Inserting, Updating, and Deleting Data 插入,更新,以及删除数据
* Inserting data 插入数据
* Updating data 更新数据
* Deleting data 删除数据
* Provider Data Types 提供者数据类型
* Alternative Forms of Provider Access 提供者访问的可选表单
* Batch access 批量访问
* Data access via intents 通过意图的数据访问
* Contract Classes 契约类(注:我猜测Contract源自契约式设计Design by contract,简称DbC,见http://en.wikipedia.org/wiki/Design_by_contract。这里的契约类简单来说就是希望其它Java应用程序链接的常量配置类)
* MIME Type Reference MIME类型引用
Key classes
关键类
ContentProvider
ContentResolver
Cursor
Uri
Related Samples
相关示例
Cursor (People) 游标(联系人)
Cursor (Phones) 游标(电话)
See also
另见
Creating a Content Provider 创建一个内容提供者
Calendar Provider 日历提供者
-------------------------------
A content provider manages access to a central repository of data. The provider and is part of an Android application, which often provides its own UI for working with the data. However, content providers are primarily intended to be used by other applications, which access the provider using a provider client object. Together, providers and provider clients offer a consistent, standard interface to data that also handles inter-process communication and secure data access.
一个内容提供者管理对数据的中央仓库的访问权。而且提供者是Android应用程序的一部分,它经常提供它自己的用户界面以使用数据来工作。然而,内容提供者主要倾向于被其它应用程序使用,它们使用一个提供者客户端对象来访问提供者。提供者和提供者客户端一起提供一个一致的,标准接口给数据,该接口还要处理进程间通信以及安全数据访问。
This topic describes the basics of the following:
这个主题描述一下内容的基础:
* How content providers work.
* 内容提供者如何工作。
* The API you use retrieve data from a content provider.
* 你用来从内容提供者取出数据的API。
* The API you use to insert, update, or delete data in a content provider.
* 你可以用于插入,更新,或删除内容提供者内的数据的API。
* Other API features that facilitate working with providers.
* 其它有助于与提供者一起工作的API特性。
-------------------------------
Overview
概览
A content provider presents data to external applications as one or more tables that are similar to the tables found in a relational database. A row represents an instance of some type of data the provider collects, and each row in the column represents an individual piece of data collected for an instance.
一个内容提供者呈现数据给外部应用程序作为一个或多个表,它们类似于在一个关系型数据库中找到的表。一行代表提供者收集的一些数据类型的一个实例,而且在该列中的每行代表对于一个实例的一个单独数据块。
For example, one of the built-in providers in the Android platform is the user dictionary, which stores the spellings of non-standard words that the user wants to keep. Table 1 illustrates what the data might look like in this provider's table:
例如,Android平台中内建的提供者之一是用户字典,它存储用户希望保留的非标准单词的拼写。表1描述数据在这个提供者的表中看起来可能像什么:
Table 1: Sample user dictionary table.
表1:示例用户字典表。
-------------------------------
* word app id frequency locale _ID
* 单词 应用id 频数 语言环境 _ID
* mapreduce user1 100 en_US 1
* precompiler user14 200 fr_FR 2
* applet user2 225 fr_CA 3
* const user1 255 pt_BR 4
* int user5 100 en_UK 5
(注:这些单词都是软件开发领域的专有名词:mapreduce映射化简、precompiler预编译器、applet Java小应用程序、const常量关键词、int整型关键词)
-------------------------------
In table 1, each row represents an instance of a word that might not be found in a standard dictionary. Each column represents some data for that word, such as the locale in which it was first encountered. The column headers are column names that are stored in the provider. To refer to a row's locale, you refer to its locale column. For this provider, the _ID column serves as a "primary key" column that the provider automatically maintains.
在表1中,每行代表一个单词实例,它可能在标准字典中找不到。每一列代表对应那个单词的一些数据,诸如最先遇到它的语言环境。列的头是存储在提供者中的列名。为了引用一个行的语言环境,你引用到它的locale列。对于此提供者,_ID列担当提供者自动维护的一个“主键”列。
-------------------------------
Note: A provider isn't required to have a primary key, and it isn't required to use _ID as the column name of a primary key if one is present. However, if you want to bind data from a provider to a ListView, one of the column names has to be _ID. This requirement is explained in more detail in the section Displaying query results.
注意:一个提供者并不必须拥有一个主键,而且它并不必须使用_ID作为主键的列名如果已经存在一个。然而,如果你希望从一个提供者中绑定数据到一个ListView,列名的其中一个必须是_ID。这个需求在章节显示查询结果中更详细地解释。
-------------------------------
Accessing a provider
访问一个提供者
An application accesses the data from a content provider with a ContentResolver client object. This object has methods that call identically-named methods in the provider object, an instance of one of the concrete subclasses of ContentProvider. The ContentResolver methods provide the basic "CRUD" (create, retrieve, update, and delete) functions of persistent storage.
一个应用程序用一个ContentResolver客户端对象来访问来自内容提供者的数据。这个对象有方法调用提供者对象,ContentProvider的具体子类之一的一个实例中的相同名称的方法。ContentResolver的方法提供持久存储的基本“CRUD”(创建,取出,更新,以及删除)函数。
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.
在客户端应用程序进程中的ContentResolver对象和拥有提供者的应用程序中的ContentProvider对象自动地处理进程间通信。ContentProvider还扮演它的数据仓库和作为表的数据的外部外观之间的抽象层。
-------------------------------
Note: To access a provider, your application usually has to request specific permissions in its manifest file. This is described in more detail in the section Content Provider Permissions
注意:为了访问一个提供者,你的应用程序通常不得不在它的清单文件中请求特定的权限。在章节内容提供者权限中更详细地描述它。
-------------------------------
For example, to get a list of the words and their locales from the User Dictionary Provider, you call ContentResolver.query(). The query() method calls the ContentProvider.query() method defined by the User Dictionary Provider. The following lines of code show a ContentResolver.query() call:
例如,为了从用户单词提供者中获取单词的列表和它们的语言环境,你调用ContentResolver.query()。query()方法调用被用户字典提供者定义的ContentProvider.query()方法。以下代码行展示一个ContentResolver.query()调用:
-------------------------------
// Queries the user dictionary and returns results
// 查询用户字典并返回结果
mCursor = getContentResolver().query(
UserDictionary.Words.CONTENT_URI, // The content URI of the words table 单词表的内容URI
mProjection, // The columns to return for each row 每行返回的列
mSelectionClause // Selection criteria 选择条件
mSelectionArgs, // Selection criteria 选择条件
mSortOrder); // The sort order for the returned rows 返回行的排序顺序
-------------------------------
Table 2 shows how the arguments to query(Uri,projection,selection,selectionArgs,sortOrder) match an SQL SELECT statement:
表2展示传给query(Uri,projection,selection,selectionArgs,sortOrder)的参数如何匹配一个SQL SELECT语句:
Table 2: Query() compared to SQL query.
表2:query()对比SQL查询。
-------------------------------
* query() argument SELECT keyword/parameter Notes
* query()参数 SELECT关键词/参数 备注
* Uri FROM table_name Uri maps to the table in the provider named table_name.
* Uri FROM <表名> Uri映射到名为<表名>提供者的表
* projection col,col,col,... projection is an array of columns that should be included for each row retrieved.
* projection 列,列,列,…… projection是一个列的数组,对于每个取出的行,这些列都应该被包括。
* selection WHERE col = value selection specifies the criteria for selecting rows.
* selection WHERE <列> = <值> selection指定行选择的条件。
* selectionArgs (No exact equivalent. Selection arguments replace ? placeholders in the selection clause.)
* selectionArgs (没有具体的等价物。选择参数替换selection子句中的'?'问号占位符。)
* sortOrder ORDER BY col,col,... sortOrder specifies the order in which rows appear in the returned Cursor.
* sortOrder ORDER BY <列>,<列>,... sortOrder指定出现在返回Cursor中的行的顺序。
-------------------------------
Content URIs
内容URI
A content URI is a URI that identifies data in a provider. Content URIs include the symbolic name of the entire provider (its authority) and a name that points to a table (a path). When you call a client method to access a table in a provider, the content URI for the table is one of the arguments.
内容URI是一个URI,它标识提供者内的数据。内容URI包含整个提供者的符号名(它的权力)以及指向一个表的名称(一个路径)。当你调用一个客户端方法以访问一个提供者中的一个表时,表的内容URI是参数之一。
In the preceding lines of code, the constant CONTENT_URI contains the content URI of the user dictionary's "words" table. The ContentResolver object parses out the URI's authority, and uses it to "resolve" the provider by comparing the authority to a system table of known providers. The ContentResolver can then dispatch the query arguments to the correct provider.
在前面的代码行中,常量CONTENT_URI包含用户字典的"words"表的内容URI。ContentResolver对象解析出URI的权力,并且使用它来通过比较权力与已知提供者的系统表以“解析”提供者。然后ContentResolver可以派发查询参数到正确的提供者。
The ContentProvider uses the path part of the content URI to choose the table to access. A provider usually has a path for each table it exposes.
ContentProvider使用内容URI的路径部分来选择要访问的表。提供者对于它暴露的每一个表通常有一个路径。
In the previous lines of code, the full URI for the "words" table is:
在前面的代码行中,"words"表的完全URI是:
-------------------------------
content://user_dictionary/words
-------------------------------
where the user_dictionary string is the provider's authority, and words string is the table's path. The string content:// (the scheme) is always present, and identifies this as a content URI.
这里user_dictionary字符串是提供者的权力,而words字符串是表的单词。字符串content://(模式)总是存在的,并且标识它为一个内容URI。
Many providers allow you to access a single row in a table by appending an ID value to the end of the URI. For example, to retrieve a row whose _ID is 4 from user dictionary, you can use this content URI:
许多提供者允许你访问一个表中的一个单一行,通过尾加一个ID值到URI的结尾。例如,为了从用户字典中取出一个_ID为4的行,你可以使用这个内容URI:
-------------------------------
Uri singleUri = ContentUri.withAppendedId(UserDictionary.Words.CONTENT_URI,4);
-------------------------------
You often use id values when you've retrieved a set of rows and then want to update or delete one of them.
当你已经取出一个行集合,然后希望更新或删除它们中的其中一个时,你经常使用id值。
-------------------------------
Note: The Uri and Uri.Builder classes contain convenience methods for constructing well-formed Uri objects from strings. The ContentUris contains convenience methods for appending id values to a URI. The previous snippet uses withAppendedId() to append an id to the UserDictionary content URI.
注意:Uri和Uri.Builder类包含用于从字符串中构造良好格式化的Uri对象的便利方法。ContentUris包含用于尾加id值到一个URI的便利方法。前面的代码片段使用withAppendedId()以尾加一个id到UserDictionary的内容URI后面。
-------------------------------
-------------------------------
Retrieving Data from the Provider
从提供者中取出数据
This section describes how to retrieve data from a provider, using the User Dictionary Provider as an example.
这个章节描述如何从提供者中取出数据,使用用户字典提供者作为一个示例。
-------------------------------
For the sake of clarity, the code snippets in this section call ContentResolver.query() on the "UI thread"". In actual code, however, you should do queries asynchronously on a separate thread. One way to do this is to use the CursorLoader class, which is described in more detail in the Loaders guide. Also, the lines of code are snippets only; they don't show a complete application.
为了清楚起见,本节中的代码片段在“用户界面线程”(注:此处貌似多了一个引号)上调用ContentResolver.query()。然而,在实际代码中,你应该在一个单独的线程上异步地执行查询。做到这一点的一种方式是使用CursorLoader类,在加载器指引中会详细地描述它。同时,代码行只是片段;它们不展示一个完整的应用程序。
-------------------------------
To retrieve data from a provider, follow these basic steps:
为了从一个提供者中取出数据,遵循这些基本步骤:
1. Request the read access permission for the provider.
1. 请求对提供者的读权限。
2. Define the code that sends a query to the provider.
2. 定义发送查询到提供者的代码。
Requesting read access permission
请求读访问权限
To retrieve data from a provider, your application needs "read access permission" for the provider. You can't request this permission at run-time; instead, you have to specify that you need this permission in your manifest, using the <uses-permission> element and the exact permission name defined by the provider. When you specify this element in your manifest, you are in effect "requesting" this permission for your application. When users install your application, they implicitly grant this request.
为了从一个提供者中取出数据,你的应用程序需要提供者的“读访问权限”。你不允许在运行期请求这个权限;取而代之,你不得不在你的清单中指定你需要这个权限,使用<uses-permission>以及提供者定义的具体的权限名称。当你在你的清单中指定这个元素时,你处于为你的应用程序“请求”这个权限的影响中。当用户安装你的应用程序时,它们显式地授权这个请求。
To find the exact name of the read access permission for the provider you're using, as well as the names for other access permissions used by the provider, look in the provider's documentation.
为了找到用于你正在使用的提供者的读访问权限的精确名称,以及被提供者使用的其它访问权限的名称,请看提供者的文档。
The role of permissions in accessing providers is described in more detail in the section Content Provider Permissions.
提供者访问权限的作用在章节内容提供者权限中更详细地描述。
The User Dictionary Provider defines the permission android.permission.READ_USER_DICTIONARY in its manifest file, so an application that wants to read from the provider must request this permission.
用户字典提供者在它的清单文件中定义android.permission.READ_USER_DICTIONARY权限,所以一个希望读取这个提供者的应用程序必须请求这个权限。
Constructing the query
构造查询
The next step in retrieving data a provider is to construct a query. This first snippet defines some variables for accessing the 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 契约类常量,对应_ID列名
UserDictionary.Words.WORD, // Contract class constant for the word column name 契约类常量,对应word列名
UserDictionary.Words.LOCALE // Contract class constant for the locale column name 契约类常量,对应locale列名
};
// Defines a string to contain the selection clause
// 定义一个字符串以包含选择子句
String mSelectionClause = null;
// Initializes an array to contain selection arguments
// 初始化一个数组以包含选择参数
String[] mSelectionArgs = {""};
-------------------------------
The next snippet shows how to use ContentResolver.query(), using the User Dictionary Provider as an example. A provider client query is similar to an SQL query, and it contains a set of columns to return, a set of selection criteria, and a sort order.
下一个代码片段展示如何使用ContentResolver.query(),,使用用户字典提供者作为一个示例。一个提供者客户端查询类似于一个SQL查询,而且它包含一组要返回的列,一组选择条件,以及一个排序顺序。
The set of columns that the query should return is called a projection (the variable mProjection).
应该返回的查询列的集合被称为投影(变量mProjection)。
The expression that specifies the rows to retrieve is split into a selection clause and selection arguments. The selection clause is a combination of logical and Boolean expressions, column names, and values (the variable mSelection). If you specify the replaceable parameter ? instead of a value, the query method retrieves the value from the selection arguments array (the variable mSelectionArgs).
指定要取出的行的表达式被切割成一个选择子句和多个选择参数。选择子句是逻辑和布尔表达式、列名以及值(变量mSelection)的组合。如果你指定可替换参数'?'代替一个值,那么query方式从选择参数数组(变量mSelectionArgs)中取出值。
In the next snippet, if the user doesn't enter a word, the selection clause is set to null, and the query returns all the words in the provider. If the user enters a word, the selection clause is set to UserDictionary.Words.Word + " = ?" and the first element of selection arguments array is set to the word the user enters.
在下一个代码片段中,如果用户不输入一个单词,那么选择子句被设置为null,而且查询返回提供者中的所有单词。如果用户输入一个单词,那么选择子句被设置为UserDictionary.Words.Word + " = ?",而且选择参数数组的第一个元素被设置为用户输入的单词。
-------------------------------
/*
* This defines a one-element String array to contain the selection argument.
* 它定义一个单元素String数组以包含选择参数
*/
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
// 设置选择子句为null将返回所有单词
mSelectionClause = null;
mSelectionArgs[0] = "";
} else {
// Constructs a selection clause that matches the word that the user entered.
// 构造一个选择子句,它匹配用户输入的单词。
mSelectionClause = " = ?";
// Moves the user's input string to the selection arguments.
// 移动用户的输入字符串到选择参数。
mSelectionArgs[0] = mSearchString;
}
// Does a query against the table and returns a Cursor object
// 对表执行一次查询并返回一个Cursor对象
mCursor = getContentResolver().query(
UserDictionary.Words.CONTENT_URI, // The content URI of the words table 单词表的内容URI
mProjection, // The columns to return for each row 每个行都返回的列
mSelectionClause // Either null, or the word the user entered 要么是null,要么是用户输入的单词
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
// 一些提供者返回null,如果一个错误发生,其余则抛出一个异常
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.
* 这里插入代码以处理错误。一定不要使用游标!
* 你肯希望调用android.util.Log.e()来记录这个错误的日志
*
*/
// If the Cursor is empty, the provider found no matches
// 如果Cursor是空的,那么提供者找不到匹配的
} 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
// 在这里插入代码以处理结果
}
-------------------------------
This query is analogous to the SQL statement:
这个查询类似于这个SQL语句:
-------------------------------
SELECT _ID, word, frequency, locale FROM words WHERE word = <userinput> ORDER BY word ASC;
SELECT _ID, word, frequency, locale FROM words WHERE word = <用户输入> ORDER BY word ASC;
-------------------------------
In this SQL statement, the actual column names are used instead of contract class constants.
在这个SQL语句中,使用实际的列名而非契约类常量。
Protecting against malicious input
对恶意输入的保护
If the data managed by the content provider is in an SQL database, including external untrusted data into raw SQL statements can lead to SQL injection.
如果被内容提供者管理的数据在一个SQL数据库中,把外部非信任数据包含进原始SQL语句可能导致SQL注入。
Consider this selection clause:
考虑这个选择子句
-------------------------------
// Constructs a selection clause by concatenating the user's input to the column name
// 通过拼接用户输入到列名来构造一个选择子句
String mSelectionClause = "var = " + mUserInput;
-------------------------------
If you do this, you're allowing the user to concatenate malicious SQL onto your SQL statement. For example, the user could enter "nothing; DROP TABLE *;" for mUserInput, which would result in the selection clause var = nothing; DROP TABLE *;. Since the selection clause is treated as an SQL statement, this might cause the provider to erase all of the tables in the underlying SQLite database (unless the provider is set up to catch SQL injection attempts).
如果你这样做,你正在允许用户拼接恶意SQL进你的SQL语句。例如,用户可以输入"nothing; DROP TABLE *;"作为mUserInput,这将导致选择子句var = nothing; DROP TABLE *;。因为选择子句被视为一个SQL语句,这可能导致提供者删除底层SQLite数据库中的所有表(除非提供者被配置为捕捉SQL注入的尝试)。
To avoid this problem, use a selection clause that uses ? as a replaceable parameter and a separate array of selection arguments. When you do this, the user input is bound directly to the query rather than being interpreted as part of an SQL statement. Because it's not treated as SQL, the user input can't inject malicious SQL. Instead of using concatenation to include the user input, use this selection clause:
为了避免这个问题,请使用一个使用问号作为可替换参数的选择子句以及一个分离的选择参数数组。当你这样做时,用户输入被直接地绑定到该查询而非被解析作为一个SQL语句的一部分。因为它不被视为SQL,所以用户输入无法注入恶意的SQL。取代使用拼接来包含用户输入,改为使用这个选择子句:
-------------------------------
// Constructs a selection clause with a replaceable parameter
// 用一个占位参数来构造一个选择子句
String mSelectionClause = "var = ?";
-------------------------------
Set up the array of selection arguments like this:
像这样配置选择参数的数组:
-------------------------------
// Defines an array to contain the selection arguments
// 定义一个数组以包含选择参数
String[] selectionArgs = {""};
-------------------------------
Put a value in the selection arguments array like this:
像这样在选择参数数组中放进一个值
-------------------------------
// Sets the selection argument to the user's input
// 设置选择参数为用户的输入
selectionArgs[0] = mUserInput;
-------------------------------
A selection clause that uses ? as a replaceable parameter and an array of selection arguments array are preferred way to specify a selection, even the provider isn't based on an SQL database.
使用问号作为一个占位参数的选择子句和一个选择参数的数组是指定选择的较好方式,即使提供者不是基于一个SQL数据库。
Displaying query results
显示查询结果
The ContentResolver.query() client method always returns a Cursor containing the columns specified by the query's projection for the rows that match the query's selection criteria. A Cursor object provides random read access to the rows and columns it contains. Using Cursor methods, you can iterate over the rows in the results, determine the data type of each column, get the data out of a column, and examine other properties of the results. Some Cursor implementations automatically update the object when the provider's data changes, or trigger methods in an observer object when the Cursor changes, or both.
ContentResolver.query()客户端方法总是返回一个Cursor,对于匹配查询的选择条件的行,它包含查询的投影指定的列。一个Cursor对象把随机读权限提供给它包含的行和列。使用Cursor的方法,你可以遍历结果中的行,决定每个列的数据类型,从一个列中取出数据,以及检查结果的其它属性。一些Cursor实现当提供者的数据改变时自动地更新对象,或者当Cursor改变时触发一个观察者对象中的方法,或者都是。
-------------------------------
Note: A provider may restrict access to columns based on the nature of the object making the query. For example, the Contacts Provider restricts access for some columns to sync adapters, so it won't return them to an activity or service.
注意:一个提供者可能限制对列的访问,基于制造查询的对象的自然特性。例如,电话簿提供者限制对一些指向同步适配器的列的访问,所以它将不把它们返回给一个活动或服务。
-------------------------------
If no rows match the selection criteria, the provider returns a Cursor object for which Cursor.getCount() is 0 (an empty cursor).
如果没有行匹配选择条件,那么提供者返回一个Cursor对象。对于它Cursor.getCount()是0(一个空的游标)。
If an internal error occurs, the results of the query depend on the particular provider. It may choose to return null, or it may throw an Exception.
如果一个内部错误发生,那么查询的结果依赖于特定的提供者。它可能选择返回null,或者它可能抛出一个Exception。
Since a Cursor is a "list" of rows, a good way to display the contents of a Cursor is to link it to a ListView via a SimpleCursorAdapter.
因为一个Cursor是一“组”行,显示一个Cursor的内容的一个好办法是通过一个SimpleCursorAdapter链接它到一个ListView。
The following snippet continues the code from the previous snippet. It creates a SimpleCursorAdapter object containing the Cursor retrieved by the query, and sets this object to be the adapter for a ListView:
以下代码片段接着前面代码片段的代码。它创建一个包含查询取出的Cursor的SimpleCursorAdapter对象,并把这个对象设置为用于一个ListView的适配器:
-------------------------------
// Defines a list of columns to retrieve from the Cursor and load into an output row
// 定义一组列以从Cursor中取出并加载进一个输出行
String[] mWordListColumns =
{
UserDictionary.Words.WORD, // Contract class constant containing the word column name 契约类常量,包含word列名
UserDictionary.Words.LOCALE // Contract class constant containing the locale column name 契约类常量,包含locale列名
};
// Defines a list of View IDs that will receive the Cursor columns for each row
// 定义将为每一行接收Cursor列的一列视图ID
int[] mWordListItems = { R.id.dictWord, R.id.locale};
// Creates a new SimpleCursorAdapter
// 创建一个新的SimpleCursorAdapter
mCursorAdapter = new SimpleCursorAdapter(
getApplicationContext(), // The application's Context object 应用程序的Context对象
R.layout.wordlistrow, // A layout in XML for one row in the ListView XML中的布局,用于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 行布局中用于视图ID的一个整型数组
0); // Flags (usually none are needed) 标志(通常什么都不需要)
// Sets the adapter for the ListView
// 设置ListView的适配器
mWordList.setAdapter(mCursorAdapter);
-------------------------------
-------------------------------
Note: To back a ListView with a Cursor, the cursor must contain a column named _ID. Because of this, the query shown previously retrieves the _ID column for the "words" table, even though the ListView doesn't display it. This restriction also explains why most providers have a _ID column for each of their tables.
注意:为了用一个Cursor支持(注:待考)一个ListView,游标必须包含一个名为_ID的列。因此,前面所示的查询取出"words"表的_ID列,即便ListView不显示它。这个限制还解释为什么大多数提供者对于它们的表的每一个都有一个_ID列。
-------------------------------
Getting data from query results
从查询结果中获取数据
Rather than simply displaying query results, you can use them for other tasks. For example, you can retrieve spellings from the user dictionary and then look them up in other providers. To do this, you iterate over the rows in the Cursor:
不是简单地显示查询结果,相反你可以把它们用于其它任务。例如,你可以从用户字典中取出拼写,然后在其它提供者中找出它们。为了做到这点,你用Cursor遍历所有行:
-------------------------------
// Determine the column index of the column named "word"
// 决定名为"word"的列的列索引
int index = mCursor.getColumnIndex(UserDictionary.Words.WORD);
/*
* Only executes if the cursor is valid. The User Dictionary Provider returns null if
* an internal error occurs. Other providers may throw an Exception instead of returning null.
* 只有在游标可用时才执行。用户字典提供者返回null如果发生一个内部错误。
* 其它提供者可能抛出一个Exception而非返回null。
*/
if (mCursor != null) {
/*
* Moves to the next row in the cursor. Before the first movement in the cursor, the
* "row pointer" is -1, and if you try to retrieve data at that position you will get an
* exception.
* 移动到游标中的下一行。在游标内第一次移动之前,
* “行指针”是-1,而且如果你尝试取出那个位置上的数据,那么你将获得一个异常。
*/
while (mCursor.moveToNext()) {
// Gets the value from the column.
// 从列中获取值
newWord = mCursor.getString(index);
// Insert code here to process the retrieved word.
// 在这里插入代码以处理取出的单词。
...
// end of while loop
// while循环的结束
}
} else {
// Insert code here to report an error if the cursor is null or the provider threw an exception.
// 在这里插入代码以报告错误,如果游标为null或者提供者抛出了一个异常。
}
-------------------------------
Cursor implementations contain several "get" methods for retrieving different types of data from the object. For example, the previous snippet uses getString(). They also have a getType() method that returns a value indicating the data type of the column.
游标的实现包含几个“get”方法以从对象中取出不同类型的数据。例如,前面的代码片段使用getString()。它们还有一个getType()方法,它返回一个指示列的数据类型的值。
-------------------------------
Content Provider Permissions
内容提供者权限
A provider's application can specify permissions that other applications must have in order to access the provider's data. These permissions ensure that the user knows what data an application will try to access. Based on the provider's requirements, other applications request the permissions they need in order to access the provider. End users see the requested permissions when they install the application.
一个提供者的应用程序可以指定其它应用程序必须拥有的权限,以访问提供者的数据。这些权限确保用户知道一个应用程序将尝试访问什么数据。基于提供者的需求,其它应用程序请求它们需要的权限以访问提供者。最终用户看到被请求的权限,当他们安装应用程序时。
If a provider's application doesn't specify any permissions, then other applications have no access to the provider's data. However, components in the provider's application always have full read and write access, regardless of the specified permissions.
如果一个提供者的应用程序不指定任何权限,那么其它应用程序没有对提供者数据的访问权。然而,提供者的应用程序的组件总是由完全的读和写访问权,不管被指定的权限是什么。
As noted previously, the User Dictionary Provider requires the android.permission.READ_USER_DICTIONARY permission to retrieve data from it. The provider has the separate android.permission.WRITE_USER_DICTIONARY permission for inserting, updating, or deleting data.
正如前面所说,用户字典提供者需要android.permission.READ_USER_DICTIONARY权限以从它里面取出数据。提供者有单独的android.permission.WRITE_USER_DICTIONARY权限用于插入,更新,或删除数据。
To get the permissions needed to access a provider, an application requests them with a <uses-permission> element in its manifest file. When the Android Package Manager installs the application, a user must approve all of the permissions the application requests. If the user approves all of them, Package Manager continues the installation; if the user doesn't approve them, Package Manager aborts the installation.
为了获取需要用于访问一个提供者的权限,一个应用程序在它的清单文件中用一个<uses-permission>元素请求它们。当Android包管理器安装应用程序时,用户必须批准应用程序请求的所有权限。如果用户批准它们全部,那么包管理器继续安装;如果用户不批准它们,那么包管理器中止安装。
The following <uses-permission> element requests read access to the User Dictionary Provider:
以下<uses-permission>元素请求对用户字典提供者的读访问权:
-------------------------------
<uses-permission android:name="android.permission.READ_USER_DICTIONARY">
-------------------------------
The impact of permissions on provider access is explained in more detail in the Security and Permissions guide.
关于提供者访问权的权限的冲击会在安全和权限指引中更详细地解释。
-------------------------------
Inserting, Updating, and Deleting Data
插入,更新和删除数据
In the same way that you retrieve data from a provider, you also use the interaction between a provider client and the provider's ContentProvider to modify data. You call a method of ContentResolver with arguments that are passed to the corresponding method of ContentProvider. The provider and provider client automatically handle security and inter-process communication.
用你从一个提供者中取出数据的相同方式,你还使用提供者客户端和提供者的ContentProvider之间的交互来修改数据。你使用传递给ContentProvider的相应方法的参数来调用ContentResolver的方法。提供者和提供者客户端自动地处理安全和进程间通信。
Inserting data
插入数据
To insert data into a provider, you call the ContentResolver.insert() method. This method inserts a new row into the provider and returns a content URI for that row. This snippet shows how to insert a new word into the User Dictionary Provider:
为了把数据插进一个提供者,你调用ContentResolver.insert()方法。这个方法把一个新行插进提供者并且返回那个行的内容URI。这个代码片段展示如何把新的单词插进用户字典提供者:
-------------------------------
// Defines a new Uri object that receives the result of the insertion
// 定义一个新的Uri对象,它取出插入的结果
Uri mNewUri;
...
// Defines an object to contain the new values to insert
// 定义一个对象以包含要插入的新值
ContentValues mNewValues = new ContentValues();
/*
* Sets the values of each column and inserts the word. The arguments to the "put"
* method are "column name" and "value"
* 设置每列的值并插入单词。
* 传入“put”方法的参数是“列名”和“值”
*/
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 用户字典内容URI
mNewValues // the values to insert 要插入的值
);
-------------------------------
The data for the new row goes into a single ContentValues object, which is similar in form to a one-row cursor. The columns in this object don't need to have the same data type, and if you don't want to specify a value at all, you can set a column to null using ContentValues.putNull().
新行的数据跑进一个单一的ContentValues对象,它类似于使用单行游标的形式。这个对象的列不需要拥有相同的数据类型,并且如果你完全不希望指定一个值,你可以使用ContentValues.putNull()设置列为null。
The snippet doesn't add the _ID column, because this column is maintained automatically. The provider assigns a unique value of _ID to every row that is added. Providers usually use this value as the table's primary key.
代码片段不添加_ID列,因为这个列被自动地维护。提供者赋予一个_ID的唯一值到每个被添加的行。提供者通常使用这个值作为表的主键。
The content URI returned in newUri identifies the newly-added row, with the following format:
在newUri中返回的内容URI标识新增行,使用以下格式:
-------------------------------
content://user_dictionary/words/<id_value>
content://user_dictionary/words/<id值>
-------------------------------
The <id_value> is the contents of _ID for the new row. Most providers can detect this form of content URI automatically and then perform the requested operation on that particular row.
<id值>是新行的_ID的内容。大多数提供者可以自动地检测这种内容URI的格式,并且然后在特定行上执行请求的操作。
To get the value of _ID from the returned Uri, call ContentUris.parseId().
为了从返回的Uri中获取_ID的值,请调用ContentUris.parseId()。
Updating data
更新数据
To update a row, you use a ContentValues object with the updated values just as you do with an insertion, and selection criteria just as you do with a query. The client method you use is ContentResolver.update(). You only need to add values to the ContentValues object for columns you're updating. If you want to clear the contents of a column, set the value to null.
为了更新一个行,你使用一个带更新值的ContentValues对象,正如你处理插入那样,以及选择条件,正如你处理查询那样。你使用的客户端方法是ContentResolver.update()。你只需要为你正在更新的列添加值到ContentValues对象。如果你希望清除一个列的内容,设置值为null。
The following snippet changes all the rows whose locale has the language "en" to a have a locale of null. The return value is the number of rows that were updated:
以下代码片段把所有locale列中拥有语言"en"的行改成locale为null的行。返回值是被更新行的数量。
-------------------------------
// Defines an object to contain the updated values
// 定义一个对象以包含更新的值
ContentValues mUpdateValues = new ContentValues();
// Defines selection criteria for the rows you want to update
// 为你希望更新的行定义选择条件
String mSelectionClause = UserDictionary.Words.LOCALE + "LIKE ?";
String[] mSelectionArgs = {"en_%"};
// Defines a variable to contain the number of updated rows
// 定义一个变量以包含更新行的数量
int 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 用户字典的内容URI
mUpdateValues // the columns to update 要更新的列
mSelectionClause // the column to select on 要选择的列
mSelectionArgs // the value to compare to 要比较的值
);
-------------------------------
You should also sanitize user input when you call ContentResolver.update(). To learn more about this, read the section Protecting against malicious input.
你还应该清除用户输入,当你调用ContentResolver.update()时。要想知道更多关于这方面的信息,请阅读章节对抗恶意输入的保护。
Deleting data
删除数据
Deleting rows is similar to retrieving row data: you specify selection criteria for the rows you want to delete and the client method returns the number of deleted rows. The following snippet deletes rows whose appid matches "user". The method returns the number of deleted rows.
删除行类似于取出行数据:你为你希望删除的行指定选择条件,而客户端方法返回被删除的数量。以下代码片段删除其appid匹配"user"的行。
-------------------------------
// Defines selection criteria for the rows you want to delete
// 为你希望删除的行定义选择条件
String mSelectionClause = UserDictionary.Words.APP_ID + " LIKE ?";
String[] mSelectionArgs = {"user"};
// Defines a variable to contain the number of rows deleted
// 定义一个变量以包含被删除的行数
int mRowsDeleted = 0;
...
// Deletes the words that match the selection criteria
// 删除匹配选择条件的单词
mRowsDeleted = getContentResolver().delete(
UserDictionary.Words.CONTENT_URI, // the user dictionary content URI 用户字典的内容URI
mSelectionClause // the column to select on 要选择的列
mSelectionArgs // the value to compare to 要比较的值
);
-------------------------------
You should also sanitize user input when you call ContentResolver.delete(). To learn more about this, read the section Protecting against malicious input.
你还应该清除用户输入,当你调用ContentResolver.delete()时。要想获取更多关于它的信息,请阅读章节对恶意输入的保护。
-------------------------------
Provider Data Types
提供者数据类型
Content providers can offer many different data types. The User Dictionary Provider offers only text, but providers can also offer the following formats:
内容提供者可以提供许多不同的数据类型。用户字典提供者只提供文本,但提供者还可以提供以下格式:
* integer
* 整型
* long integer (long)
* 长整型(长)
* floating point
* 浮点
* long floating point (double)
* 长浮点(双精度)
Another data type that providers often use is Binary Large OBject (BLOB) implemented as a 64KB byte array. You can see the available data types by looking at the Cursor class "get" methods.
另一种提供者经常使用的数据类型是二进制大数据(BLOB),被实现为一个64KB字节数组。你可以通过查看Cursor类的“get”方法看到可用的数据类型
The data type for each column in a provider is usually listed in its documentation. The data types for the User Dictionary Provider are listed in the reference documentation for its contract class UserDictionary.Words (contract classes are described in the section Contract Classes). You can also determine the data type by calling Cursor.getType().
在一个提供者中每一列的数据类型通常被列举在它的文档中。用户字典提供者的数据类型被列举在它的契约类UserDictionary.Words的参考文档中(契约类在章节契约类中描述)。你还可以通过调用Cursor.getType()确定数据类型。
Providers also maintain MIME data type information for each content URI they define. You can use the MIME type information to find out if your application can handle data that the provider offers, or to choose a type of handling based on the MIME type. You usually need the MIME type when you are working with a provider that contains complex data structures or files. For example, the ContactsContract.Data table in the Contacts Provider uses MIME types to label the type of contact data stored in each row. To get the MIME type corresponding to a content URI, call ContentResolver.getType().
提供者还维护它们顶一个的每个内容URI的MIME数据类型信息。你可以使用MIME类型信息以找出你的应用程序是否可以处理提供者提供的数据,或者基于MIME类型选择一类处理。你通常需要MIME类型,当你正在用一个包含复杂数据结构或文件的提供者工作。例如,电话簿提供者中的ContactsContract.Data使用MIME类型以标出存储在每一行中的电话簿数据的类型。为了获取对应一个内容URI的MIME类型,请调用ContentResolver.getType()。
The section MIME Type Reference describes the syntax of both standard and custom MIME types.
章节MIME类型引用中描述标准和自定MIME类型的语法。
-------------------------------
Alternative Forms of Provider Access
提供者访问的可选表单
Three alternative forms of provider access are important in application development:
提供者访问的三种可选形式在应用程序开发中是重要的:
* Batch access: You can create a batch of access calls with methods in the ContentProviderOperation class, and then apply them with ContentResolver.applyBatch().
* 批量访问:你可以用ContentProviderOperation类的方法创建一批访问调用,并且然后用ContentResolver.applyBatch()应用它们。
* Asynchronous queries: You should do queries in a separate thread. One way to do this is to use a CursorLoader object. The examples in the Loaders guide demonstrate how to do this.
* 异步查询:你应该在一个单独的线程中执行查询。这样做的一种方式是使用一个CursorLoader对象。加载器指引中的示例演示如何做到这点。
* Data access via intents: Although you can't send an intent directly to a provider, you can send an intent to the provider's application, which is usually the best-equipped to modify the provider's data.
* 通过意图的数据访问:虽然你不允许直接发送一个意图到一个提供者,但是你可以发送一个意图到提供者的应用程序,它通常是最胜任修改提供者的数据。
Batch access and modification via intents are described in the following sections.
通过意图批量访问和修改在以下章节中描述。
Batch access
批量访问
Batch access to a provider is useful for inserting a large number of rows, or for inserting rows in multiple tables in the same method call, or in general for performing a set of operations across process boundaries as a transaction (an atomic operation).
对一个提供者的批量访问,有助于插入大量的行,或者在相同的方法调用中把行插入到多个表,或者通常地跨进程边界执行一组操作作为一个事务(一个原子操作)。
To access a provider in "batch mode", you create an array of ContentProviderOperation objects and then dispatch them to a content provider with ContentResolver.applyBatch(). You pass the content provider's authority to this method, rather than a particular content URI, which allows each ContentProviderOperation object in the array to work against a different table. A call to ContentResolver.applyBatch() returns an array of results.
为了以“批量模式”访问一个提供者,你创建一个ContentProviderOperation数组,然后用ContentResolver.applyBatch()派发它们到一个内容提供者。你把内容提供者的权力传递给这个方法,而非一个特定的内容URI,它允许数组中每个ContentProviderOperation处理一个不同的表。对ContentResolver.applyBatch()的一次调用返回结果的数组。
The description of the ContactsContract.RawContacts contract class includes a code snippet that demonstrates batch insertion. The Contact Manager sample application contains an example of batch access in its ContactAdder.java source file.
ContactsContract.RawContacts契约类的描述包含一个代码片段,它演示批量插入。电话簿管理器示例应用在它的ContactAdder.java源码中包含一个批量访问示例。
-------------------------------
Displaying data using a helper app
使用一个辅助器应用显示数据
If your application does have access permissions, you still may want to use an intent to display data in another application. For example, the Calendar application accepts an ACTION_VIEW intent, which displays a particular date or event. This allows you to display calendar information without having to create your own UI. To learn more about this feature, see the Calendar Provider guide.
如果你的应用程序没有访问权限,那么你仍然可能希望使用一个意图来显示另一个应用程序的数据。例如,日历应用程序接受一个ACTION_VIEW意图,它显示一个特定日期(注:约会)或事件。它允许你显示日历信息而不必创建你自己的用户界面。要想学习关于这个特性的更多信息,请见日历提供者指引。
The application to which you send the intent doesn't have to be the application associated with the provider. For example, you can retrieve a contact from the Contact Provider, then send an ACTION_VIEW intent containing the content URI for the contact's image to an image viewer.
你把意图发送到的应用程序不必是关联至该提供者的应用程序。例如,你可以从电话簿提供者中取出一个电话簿,然后发送一个包含对应该电话簿图片的内容URI的ACTION_VIEW意图到一个图片查看器。
-------------------------------
Data access via intents
通过意图的数据访问
Intents can provide indirect access to a content provider. You allow the user to access data in a provider even if your application doesn't have access permissions, either by getting a result intent back from an application that has permissions, or by activating an application that has permissions and letting the user do work in it.
意图可以提供对一个内容提供者的间接访问。你允许用户访问提供者里的数据,即便你的应用程序没有访问权限,要么通过获取一个返回自一个有权限的应用程序的结果意图,要么通过激活一个拥有权限的应用程序并让用户在它里面工作。
Getting access with temporary permissions
用临时权限获取访问权
You can access data in a content provider, even if you don't have the proper access permissions, by sending an intent to an application that does have the permissions and receiving back a result intent containing "URI" permissions. These are permissions for a specific content URI that last until the activity that receives them is finished. The application that has permanent permissions grants temporary permissions by setting a flag in the result intent:
你可以访问一个内容提供者的数据,即便你没有合适的访问权限,通过发送一个意图到一个拥有权限的应用程序并收回一个包含“URI”权限的结果意图。这些是用于一个特定内容URI的权限,它持续直至接收它们的活动被完成。拥有永久权限的应用程序通过在结果意图中设置一个标志来授予临时权限:
* Read permission: FLAG_GRANT_READ_URI_PERMISSION
* 读权限:FLAG_GRANT_READ_URI_PERMISSION
* Write permission: FLAG_GRANT_WRITE_URI_PERMISSION
* 写权限:FLAG_GRANT_WRITE_URI_PERMISSION
-------------------------------
Note: These flags don't give general read or write access to the provider whose authority is contained in the content URI. The access is only for the URI itself.
注意:这些标志不给予一般的读或写权限给在内容URI中包含权力的提供者。访问权只用于URI自身。
-------------------------------
A provider defines URI permissions for content URIs in its manifest, using the android:grantUriPermission attribute of the <provider> element, as well as the <grant-uri-permission> child element of the <provider> element. The URI permissions mechanism is explained in more detail in the Security and Permissions guide, in the section "URI Permissions".
一个提供者在它的清单中为内容URI定义URI权限。使用<provider>元素的android:grantUriPermission属性,以及用于<provider>元素的<grant-uri-permission>子元素。在安全与权限指引的章节“URI权限”中更详细地解释URI权限机制。
For example, you can retrieve data for a contact in the Contacts Provider, even if you don't have the READ_CONTACTS permission. You might want to do this in an application that sends e-greetings to a contact on his or her birthday. Instead of requesting READ_CONTACTS, which gives you access to all of the user's contacts and all of their information, you prefer to let the user control which contacts are used by your application. To do this, you use the following process:
例如,你可以取出电话簿提供者中的一条电话簿的数据,即便你没有READ_CONTACTS权限。你可能希望在一个在他和她生日时发送电子贺卡给一个联系方式的应用程序中做到这点。不是请求READ_CONTACTS,它给予你所有用户电话簿以及他们的所有信息的访问权,你更倾向于让用户控制你的应用程序使用哪个电话簿。为了做到这点,你使用以下步骤:
1. Your application sends an intent containing the action ACTION_PICK and the "contacts" MIME type CONTENT_ITEM_TYPE, using the method startActivityForResult().
1. 你的应用程序发送一个包含动作ACTION_PICK和“电话簿”的MIME类型CONTENT_ITEM_TYPE的意图,使用方法startActivityForResult()。
2. Because this intent matches the intent filter for the People app's "selection" activity, the activity will come to the foreground.
2. 因为这个意图匹配用于联系人应用的“选择”活动的意图过滤器,所以活动将跑到前台。
3. In the selection activity, the user selects a contact to update. When this happens, the selection activity calls setResult(resultcode, intent) to set up a intent to give back to your application. The intent contains the content URI of the contact the user selected, and the "extras" flags FLAG_GRANT_READ_URI_PERMISSION. These flags grant URI permission to your app to read data for the contact pointed to by the content URI. The selection activity then calls finish() to return control to your application.
3. 在选择活动中,用户选择一个要更新的电话簿。当它发生时,选择活动调用setResult(resultcode, intent)以配置一个意图以给回你的应用程序。意图包含用户选择的电话簿的内容URI,以及“额外”标志FLAG_GRANT_READ_URI_PERMISSION。这些标志授权URI权限给你的应用以读取内容URI指向的电话簿的数据。然后选择活动调用finish()以返回控制权给你的应用程序。
4. Your activity returns to the foreground, and the system calls your activity's onActivityResult() method. This method receives the result intent created by the selection activity in the People app.
4. 你的活动返回到前台,而系统调用你的活动的onActivityResult()方法。这个方法接收在联系人应用中选择活动创建的结果意图。
5. With the content URI from the result intent, you can read the contact's data from the Contacts Provider, even though you didn't request permanent read access permission to the provider in your manifest. You can then get the contact's birthday information or his or her email address and then send the e-greeting.
5. 使用来自结果意图的内容URI,你可以从电话簿提供者中读取电话簿的数据,即便你在你的清单中不请求对提供者的永久读访问权限。然后你可以获取电话簿的生日信息或(注:这里貌似应该是“和”)他或她的电子邮件地址然后发送电子贺卡。
Using another application
使用另一个应用程序
A simple way to allow the user to modify data to which you don't have access permissions is to activate an application that has permissions and let the user do the work there.
允许用户修改你没有访问权限的数据的简单方式是激活一个拥有权限的应用程序并且让用户在那里工作。
For example, the Calendar application accepts an ACTION_INSERT intent, which allows you to activate the application's insert UI. You can pass "extras" data in this intent, which the application uses to pre-populate the UI. Because recurring events have a complex syntax, the preferred way of inserting events into the Calendar Provider is to activate the Calendar app with an ACTION_INSERT and then let the user insert the event there.
例如,日历应用程序接受一个ACTION_INSERT意图,它允许你激活应用程序的插入的用户界面。你可以在这个意图中传递“额外”数据,该应用程序使用它来预填充用户界面。因为循环事件有一个复杂的语法,把事件插进日历提供者的首选方式是用一个ACTION_INSERT激活日历应用,然后让用户在那里插入事件。
-------------------------------
Contract Classes
契约类
A contract class defines constants that help applications work with the content URIs, column names, intent actions, and other features of a content provider. Contract classes are not included automatically with a provider; the provider's developer has to define them and then make them available to other developers. Many of the providers included with the Android platform have corresponding contract classes in the package android.provider.
一个契约类定义帮助应用程序用内容URI,列名,意图动作,以及内容提供者的其它特性工作的常量。契约类不随一个提供者自动地被包含;提供者的开发者不得不定义它们,然后让它们对于其它开发者可用。有许多随Android平台包含的提供者拥有相应的契约类,位于包android.provider中。
For example, the User Dictionary Provider has a contract class UserDictionary containing content URI and column name constants. The content URI for the "words" table is defined in the constant UserDictionary.Words.CONTENT_URI. The UserDictionary.Words class also contains column name constants, which are used in the example snippets in this guide. For example, a query projection can be defined as:
例如,用户字典提供者拥有一个契约类UserDictionary包含内容URI和列名常量。"words"表的内容URI被定义在常量UserDictionary.Words.CONTENT_URI中。UserDictionary.Words类也包含列名常量,在这个指引的示例片段中使用它们。例如,一个查询投影可以被定义为:
-------------------------------
String[] mProjection =
{
UserDictionary.Words._ID,
UserDictionary.Words.WORD,
UserDictionary.Words.LOCALE
};
-------------------------------
Another contract class is ContactsContract for the Contacts Provider. The reference documentation for this class includes example code snippets. One of its subclasses, ContactsContract.Intents.Insert, is a contract class that contains constants for intents and intent data.
另一个契约类是用于电话簿提供者的ContactsContract。这个类的参考文档包含示例代码片段。它的子类之一,ContactsContract.Intents.Insert,是一个包含用于意图和意图数据的常量的契约类。
-------------------------------
MIME Type Reference
MIME类型引用
Content providers can return standard MIME media types, or custom MIME type strings, or both.
内容提供者可以返回标准MIME媒体类型,或自定义MIME类型字符串,或两者都有。
MIME types have the format
MIME类型拥有这样的格式
-------------------------------
type/subtype
<类型>/<子类型>
-------------------------------
For example, the well-known MIME type text/html has the text type and the html subtype. If the provider returns this type for a URI, it means that a query using that URI will return text containing HTML tags.
例如,有名的MIME类型text/html拥有text类型和html子类型。如果提供者返回这个类型作为一个URI,那么它意味着一个使用那个URI的查询将返回包含HTML标签的文本。
Custom MIME type strings, also called "vendor-specific" MIME types, have more complex type and subtype values. The type value is always
自定义MIME类型设置,也被称为“供应商特定”的MIME类型,有更复杂的类型和子类型值。类型值总是
-------------------------------
vnd.android.cursor.dir
-------------------------------
for multiple rows, or
用于多个行,或者
-------------------------------
vnd.android.cursor.item
-------------------------------
for a single row.
用于单一行。
The subtype is provider-specific. The Android built-in providers usually have a simple subtype. For example, the when the Contacts application creates a row for a telephone number, it sets the following MIME type in the row:
子类型是特定于提供者的。Android内建的提供者总是有一个简单的子类型。例如,(注:这里貌似多了个the)当电话簿应用程序为一个电话号码创建一个行时,它在行中设置以下MIME类型。
-------------------------------
vnd.android.cursor.item/phone_v2
-------------------------------
Notice that the subtype value is simply phone_v2.
注意子类型的值简单的是phone_v2。
Other provider developers may create their own pattern of subtypes based on the provider's authority and table names. For example, consider a provider that contains train timetables. The provider's authority is com.example.trains, and it contains the tables Line1, Line2, and Line3. In response to the content URI
其它提供者开发者可能创建他们自己的子类型模式,基于提供者的权力和表名。例如,考虑一个包含火车时间表的提供者。提供者的权力是com.example.trains,而它包含表Line1,Line2,和Line3。对应内容URI
-------------------------------
content://com.example.trains/Line1
-------------------------------
for table Line1, the provider returns the MIME type
用于表Line1,提供者返回MIME类型
-------------------------------
vnd.android.cursor.dir/vnd.example.line1
-------------------------------
In response to the content URI
对应内容URI
-------------------------------
content://com.example.trains/Line2/5
-------------------------------
for row 5 in table Line2, the provider returns the MIME type
用于表Line2中的行5,提供者返回MIME类型
-------------------------------
vnd.android.cursor.item/vnd.example.line2
-------------------------------
Most content providers define contract class constants for the MIME types they use. The Contacts Provider contract class ContactsContract.RawContacts, for example, defines the constant CONTENT_ITEM_TYPE for the MIME type of a single raw contact row.
大多数内容提供者为你使用的MIME类型定义契约类常量。例如,电话簿提供者的契约类ContactsContract.RawContacts为单一原始电话簿行的MIME类型定义常量CONTENT_ITEM_TYPE。
Content URIs for single rows are described in the section Content URIs.
单一行的内容URI在章节内容URI中描述。
Except as noted, this content is licensed under Apache 2.0. For details and restrictions, see the Content License.
除特别说明外,本文在Apache 2.0下许可。细节和限制请参考内容许可证。
Android 4.0 r1 - 27 Jan 2012 1:49
-------------------------------
Portions of this page are modifications based on work created and shared by the Android Open Source Project and used according to terms described in the Creative Commons 2.5 Attribution License.
(此页部分内容基于Android开源项目,以及使用根据创作公共2.5来源许可证描述的条款进行修改)
(本人翻译质量欠佳,请以官方最新内容为准,或者参考其它翻译版本:
* ソフトウェア技術ドキュメントを勝手に翻訳
http://www.techdoctranslator.com/android
* Ley's Blog
http://leybreeze.com/blog/
* 农民伯伯
http://www.cnblogs.com/over140/
* Android中文翻译组
http://androidbox.sinaapp.com/
)