Content Providers内容提供者
 
 
In this document本文档
1. Content provider basics 内容提供者基础
2. Querying a content provider 查询内容提供者
3. Modifying data in a provider 修改内容提供者数据
4. Creating a content provider 创建内容提供者
5. Content URI summary 内容URI总结
Key classes关键类
1. ContentProvider 内容提供者
2. ContentResolver 内容解释者
3. Cursor 指示器(指针)
See also
1. Calendar Provider 日历提供者
Content providers store and retrieve data and make it accessible to all applications. They're the only way to share data across applications; there's no common storage area that all Android packages can access.
内容提供者存储和取回数据,并让所有的应用可以访问它.它是应用之间唯一共享数据的方法.系统中没有所有应用都可以访问的通用存储区域.
Android ships with a number of content providers for common data types (audio, video, images, personal contact information, and so on). You can see some of them listed in the android.provider package. You can query these providers for the data they contain (although, for some, you must acquire the proper permission to read the data).
Android带有大量普通数据的内容提供者(音乐,视频,图像,个人通信录信息,等等).你可以在 android.provider 包中看到.你可以内容提供者所包含的数据(但是,对于某些数据,你需要取得相应的数据读取权限)
Note: Android 4.0 introduces the Calendar Provider. For more information, see Calendar Provider.注意4.0包括了日历提供者,详细情况,请看 Calendar Provider
If you want to make your own data public, you have two options: You can create your own content provider (a ContentProvider subclass) or you can add the data to an existing provider — if there's one that controls the same type of data and you have permission to write to it.
如果你想让公开你的数据,你有两个选择:你可以创建自己的内空提供者(一个ContentProvider 子类),或者把数据加入到已存在的内空提供者— (如果有一个)控制数据的同样的类型并你有权限写入它.(不好翻译)
This document is an introduction to using content providers. After a brief discussion of the fundamentals, it explores how to query a content provider, how to modify data controlled by a provider, and how to create a content provider of your own.
本文档是使用内容提供者的介绍.在简短的讨论基础之后,它展示了怎样查询内容提供者,修改由内容提供控制的数据,及如何创建自己的内容提供者.
Content Provider Basics内容提供者基础
How a content provider actually stores its data under the covers is up to its designer. But all content providers implement a common interface for querying the provider and returning results — as well as for adding, altering, and deleting data.
设计者决定内容提供者如何在掩护下实际的存储他的数据.但是内容提供者实现一个通用接口来查询内容提供者并返回结果—比如增加,更改,及删除
It's an interface that clients use indirectly, most generally through ContentResolver objects. You get a ContentResolver by calling getContentResolver() from within the implementation of an Activity or other application component:
它是一个用户间接使用的接口,大多情况是通过一个 ContentResolver对象.你可以在活动的实现或者其他应用组件,调用getContentResolver()方法,得到一个内容解释者.
ContentResolver cr = getContentResolver();
You can then use the ContentResolver's methods to interact with whatever content providers you're interested in.
然后,你可以用这个内容解释者的方法,与你想要的内容提供者交互.
When a query is initiated, the Android system identifies the content provider that's the target of the query and makes sure that it is up and running. The system instantiates all ContentProvider objects; you never need to do it on your own. In fact, you never deal directly with ContentProvider objects at all. Typically, there's just a single instance of each type of ContentProvider. But it can communicate with multiple ContentResolver objects in different applications and processes. The interaction between processes is handled by the ContentResolver and ContentProvider classes.
当一个查询被实例化,Android系统确定查询的目标,即内容提供者,并确保它是运行的.系统实例化所有的内容提供者对象.你永远不需要自己初始化.事实上,你从不直接处理内容提供者对象.典型的,是每个内容提供者的单个实例.但是它可以在不同应用和进程中与多个内容解释者难通信.在进之间的交流是由内容解释者和内容提供者的类来处理的.
The data model数据模型
Content providers expose their data as a simple table on a database model, where each row is a record and each column is data of a particular type and meaning. For example, information about people and their phone numbers might be exposed as follows:
内容提供者把它的数解释成一个基于数据库模型上的简单的表格,每一行是一条记录,每一列是特定类型的数据和它所表示的意义.比如,一个关于人和他的电话号码的表格可能是下面这个样子:

_ID
NUMBER
NUMBER_KEY
LABEL
NAME
TYPE
13
(425) 555 6677
425 555 6677
Kirkland office
Bully Pulpit
TYPE_WORK
44
(212) 555-1234
212 555 1234
NY apartment
Alan Vain
TYPE_HOME
45
(212) 555-6657
212 555 6657
Downtown office
Alan Vain
TYPE_MOBILE
53
201.555.4433
201 555 4433
Love Nest
Rex Cars
TYPE_HOME

Every record includes a numeric _ID field that uniquely identifies the record within the table. IDs can be used to match records in related tables — for example, to find a person's phone number in one table and pictures of that person in another.
每个记录标有_ID的域,是表格中该数据唯一的标识.在关系表中,IDs常用于匹配记录—例如,在表中找出某个人的电话号码,及他的照片.
A query returns a Cursor object that can move from record to record and column to column to read the contents of each field. It has specialized methods for reading each type of data. So, to read a field, you must know what type of data the field contains. (There's more on query results and Cursor objects later.)
一个查询返回一个 Cursor对象,该对象可以从一个记录移动到另一个记录,并且可以列的形式读取每个域的内容.它有读取每种数据类型的特殊方法.所以,要读取一个域,你必须知道该域包括的数据是什么类型.(后面有更多关于Cursor对象和查询结果的讨论)
URIs
Each content provider exposes a public URI (wrapped as a Uri object) that uniquely identifies its data set. A content provider that controls multiple data sets (multiple tables) exposes a separate URI for each one. All URIs for providers begin with the string "content://". The content: scheme identifies the data as being controlled by a content provider.
每个内容提供者显示一个公用的URI(当做一个Uri 对象包裹起来),它是数据集的标识.一个内容提供者控制多个数据集合(多个表格),并把每一个解释成一个独立的URI.内容提供者的所有的URIs都以字串"content://"开始。内容:cheme identifies the data as being controlled by a content provider.
If you're defining a content provider, it's a good idea to also define a constant for its URI, to simplify client code and make future updates cleaner. Android defines CONTENT_URI constants for all the providers that come with the platform. For example, the URI for the table that matches phone numbers to people and the URI for the table that holds pictures of people (both controlled by the Contacts content provider) are:
如果你定义一个内容提供者,最好为它定义一人URI常量,以指定客户代码并为将来更新清除。Android为平台自带的所有内容提供者定义了CONTENT_URI 常量.比如,匹配某人电话的表格的URI,匹配某人所捅有的图片的URI(它们都由内通信录内容提供者所控制)
android.provider.Contacts.Phones.CONTENT_URI android.provider.Contacts.Photos.CONTENT_URI
The URI constant is used in all interactions with the content provider. Every ContentResolver method takes the URI as its first argument. It's what identifies which provider the ContentResolver should talk to and which table of the provider is being targeted.
这个URI常用于与内容提供者进行所有的交互。每个ContentResolver 方法,都把它当成第一个参数。它指定了内容解释者与那个内容提供者对话,内容提供者定位到那个表格。
Querying a Content Provider查询内容提供者
You need three pieces of information to query a content provider: 要查询一个内容提供者,你需要三个方面的信息
·         The URI that identifies the provider 指定提供者的URI
·         The names of the data fields you want to receive 你想要接收的数据域名字
·         The data types for those fields 这些域的数据类型。
If you're querying a particular record, you also need the ID for that record.如果你要查询一个特定记录,你还需要该记录的ID.
Making the query生成查询
To query a content provider, you can use either the ContentResolver.query() method or the Activity.managedQuery() method. Both methods take the same set of arguments, and both return a Cursor object. However, managedQuery() causes the activity to manage the life cycle of the Cursor. A managed Cursor handles all of the niceties, such as unloading itself when the activity pauses, and requerying itself when the activity restarts. You can ask an Activity to begin managing an unmanaged Cursor object for you by calling Activity.startManagingCursor().
要查询内容提供者。你可以用ContentResolver.query()方法,或者Activity.managedQuery()方法。它们都采用同相样的参数集,并都返回一个Cursor对象。但是,方法导致活动管理Cursor的生命周期。一个管理的Cursor处理所有的细节,比如当活动暂停时不加载它自己,当活动重新启动时,重新查询它自己。你可以调用Activity.startManagingCursor()方法,要求活动管理没有管理的Cursor对象。
The first argument to either query() or managedQuery() is the provider URI — the CONTENT_URI constant that identifies a particular ContentProvider and data set (see URIs earlier).
query() 或者 managedQuery()方法的,第一个参数是URI,标识某个内容提供者及数据集的CONTENT_URI 常量.
To restrict a query to just one record, you can append the _ID value for that record to the URI — that is, place a string matching the ID as the last segment of the path part of the URI. For example, if the ID is 23, the URI would be:
为了限定一个查询仅查询一个记录,你可以给URI追加一个_ID值—放置一个匹配ID字串,做为URI部分的最后路径段。
content://. . . ./23
There are some helper methods, particularly ContentUris.withAppendedId() and Uri.withAppendedPath(), that make it easy to append an ID to a URI. Both are static methods that return a Uri object with the ID added. So, for example, if you were looking for record 23 in the database of people contacts, you might construct a query as follows:
有一些辅助方法,特别是ContentUris.withAppendedId() 方法和Uri.withAppendedPath()方法,它们使得追加ID到URI更加容易。它们都是静态方法,并且返回一个带追加ID的Uri对象。所以如果,你要在通信录数据库中查询记录23,你可能会这样构建一个查询:
import android.provider.Contacts.People;
import android.content.ContentUris;
import android.net.Uri;
import android.database.Cursor;
 
// Use the ContentUris method to produce the base URI for the contact with _ID == 23.
//使用方法,产生一个基于Uri且通信录_ID等于23的。
Uri myPerson = ContentUris.withAppendedId(People.CONTENT_URI, 23);
 
// Alternatively, use the Uri method to produce the base URI.
// It takes a string rather than an integer.
// 一个可选方法,用Uri方法,产生一个基于URI的,它采用字串,而不是一个整型值
Uri myPerson = Uri.withAppendedPath(People.CONTENT_URI, "23");
 
// Then query for this specific record:查询这个指定的记录:
Cursor cur = managedQuery(myPerson, null, null, null, null);
The other arguments to the query() and managedQuery() methods delimit the query in more detail. They are:
query()managedQuery()方法的,其他参数,更详细的限定查询内容:
·         The names of the data columns that should be returned. A null value returns all columns. Otherwise, only columns that are listed by name are returned. All the content providers that come with the platform define constants for their columns. For example, the android.provider.Contacts.Phones class defines constants for the names of the columns in the phone table illustrated earlier — _ID, NUMBER, NUMBER_KEY, NAME, and so on.
应该返回数据列的名字。一个null值,返回所有列。否则,只有由名字指定的列被返回。平台的所有内容提供者,为它们的列定义了常量。比如 android.provider.Contacts.Phones定义了上电话面图表中的列名的常量—ID, NUMBER, NUMBER_KEY, NAME,等等。
·         A filter detailing which rows to return, formatted as an SQL WHERE clause (excluding the WHERE itself). A null value returns all rows (unless the URI limits the query to a single record).
还有过滤到那一行要返回,格式化似一个SQL WHERE簇(包括WHERE自己),null返回所的行(除非URI限定了查询单个记录)
·         Selection arguments.选择参数
·         A sorting order for the rows that are returned, formatted as an SQL ORDER BY clause (excluding the ORDER BY itself). A null value returns the records in the default order for the table, which may be unordered.
返回行的排列顺序,以簇格式化似SQL ORDER(包括 ORDER BY 它自己),一个空值返回表格中记录的默认顺序,可能是无序的。
Let's look at an example query to retrieve a list of contact names and their primary phone numbers: 让我们看一个,查询读取通信录名字和它的主要电话号码的列表:
import android.provider.Contacts.People;
import android.database.Cursor;
 
// Form an array specifying which columns to return. 通过一个数组指定要返回的列
String[] projection = new String[] {
                             People._ID,
                             People._COUNT,
                             People.NAME,
                             People.NUMBER
                          };
 
// Get the base URI for the People table in the Contacts content provider.
Uri contacts = People.CONTENT_URI;
 
// Make the query. 
Cursor managedCursor = managedQuery(contacts,
                         projection, // Which columns to return 要返回那些列
                         null,       // Which rows to return (all rows)返回的行(所有行)
                         null,       // Selection arguments (none)(可选参数,无)
                         // Put the results in ascending order by name(以名字降序排列)
                         People.NAME + " ASC");
This query retrieves data from the People table of the Contacts content provider. It gets the name, primary phone number, and unique record ID for each contact. It also reports the number of records that are returned as the _COUNT field of each record.
这个查询读取通信录内容提供者中人员表的数据。它读取每个通信录的名字,主要电话号码,及唯一的记录ID.它也报道每个记录的_COUNT域的,记录值。
The constants for the names of the columns are defined in various interfaces — _ID and _COUNT in BaseColumns, NAME in PeopleColumns, and NUMBER in PhoneColumns. The Contacts.People class implements each of these interfaces, which is why the code example above could refer to them using just the class name.
列名的常量是以各种接口定义的-_ID_COUNT基于 BaseColumns NAME PeopleColumns, NUMBER PhoneColumns.Contacts.People 实现了这些接口,这也是为什么上面例子代码中只使用一个类名就可以引用它们的原因。
What a query returns一个查询返回什么
A query returns a set of zero or more database records. The names of the columns, their default order, and their data types are specific to each content provider. But every provider has an _ID column, which holds a unique numeric ID for each record. Every provider can also report the number of records returned as the _COUNT column; its value is the same for all rows.
一个查询返回一组0值,或者更多数据记录。列名,它们的顺序,由内容提供者指定的它们的数据类型。但是每一个提供者有一个_ID列,它指示了每个记录的唯一的ID.每个内容提供者也报道记录的个数,返回_COUNT列,它的值对所有的行都是一样的。
Here is an example result set for the query in the previous section: 这是上面章节查询结果集的一个例子

_ID
_COUNT
NAME
NUMBER
44
3
Alan Vain
212 555 1234
13
3
Bully Pulpit
425 555 6677
53
3
Rex Cars
201 555 4433

The retrieved data is exposed by a Cursor object that can be used to iterate backward or forward through the result set. You can use this object only to read the data. To add, modify, or delete data, you must use a ContentResolver object.
取回的数据由一个Cursor 对象解释,它可以向后或向前迭代记录集。你只能用这个对象读取数据,要添加,修改,或者删除数据,你必须要用一个ContentResolver 内容解释对象。
Reading retrieved data读取取回数据。
The Cursor object returned by a query provides access to a recordset of results. If you have queried for a specific record by ID, this set will contain only one value. Otherwise, it can contain multiple values. (If there are no matches, it can also be empty.) You can read data from specific fields in the record, but you must know the data type of the field, because the Cursor object has a separate method for reading each type of data — such as getString(), getInt(), and getFloat(). (However, for most types, if you call the method for reading strings, the Cursor object will give you the String representation of the data.) The Cursor lets you request the column name from the index of the column, or the index number from the column name.
由查询返回的Cursor 对象,访问结果的记录集。如果你查询由ID指定的记录,这个集只包含一个值。否则,它能包含多个集(如果没有匹配,也可能是空)。你可以读取记录中的指定域,但你必须知道域的记录类型,因为Cursor对象为每个数据类型有一个独立的方法-比如getString(), getInt(), 和getFloat().(然而,大多数类型,如果你调用读字串方法,Cursor对象将给一个该数据所表示的字串值),Cursor对象可让你从列索引读取列名,或者从列名读取列索引
The following snippet demonstrates reading names and phone numbers from the query illustrated earlier: 下面的代码片断演示了读取上面图表中的名字和电话号码。
import android.provider.Contacts.People;
 
private void getColumnData(Cursor cur){ 
    if (cur.moveToFirst()) {
 
        String name; 
        String phoneNumber; 
        int nameColumn = cur.getColumnIndex(People.NAME); 
        int phoneColumn = cur.getColumnIndex(People.NUMBER);
        String imagePath; 
    
        do {
            // Get the field values
            name = cur.getString(nameColumn);
            phoneNumber = cur.getString(phoneColumn);
           
            // Do something with the values. 
            ... 
 
        } while (cur.moveToNext());
 
    }
}
If a query can return binary data, such as an image or sound, the data may be directly entered in the table or the table entry for that data may be a string specifying a content: URI that you can use to get the data. In general, smaller amounts of data (say, from 20 to 50K or less) are most often directly entered in the table and can be read by calling Cursor.getBlob(). It returns a byte array.
如果查询返回二进制数据,比如图像或者声音,数据能直接存储在表格中,或者表格的入点可能是指定一个内容的字串:你可以用来读取数据的URI。通常少量的数据(20到50k之间或者更少)直接存储在表格中,并且可以通过调用 Cursor.getBlob()读取。它返回一个字节数组。
If the table entry is a content: URI, you should never try to open and read the file directly (for one thing, permissions problems can make this fail). Instead, you should call ContentResolver.openInputStream() to get an InputStream object that you can use to read the data.
如果表格的入点是一个内容:URI,你应永不要直接找开并读取文件(原因是,权限问题可能让其失败)。你应该调用ContentResolver.openInputStream() 方法,获取一个输入流InputStream 对象,来读取数据。
Modifying Data修改数据。
Data kept by a content provider can be modified by: 内容提供者保持的数据可做如下修改
·         Adding new records 增加新记录
·         Adding new values to existing records 添加新值到存在的记录
·         Batch updating existing records 批处理更新数据
·         Deleting records 删除数据
All data modification is accomplished using ContentResolver methods. Some content providers require a more restrictive permission for writing data than they do for reading it. If you don't have permission to write to a content provider, the ContentResolver methods will fail.
所有的修改操作由ContentResolver 方法完成。一些内容提供者写入数据比读取数据要求更多的权限。如果你没有写某个内容提供者的权限,ContentResolver 内容解释方法将失败。
Adding records添加记录
To add a new record to a content provider, first set up a map of key-value pairs in a ContentValues object, where each key matches the name of a column in the content provider and the value is the desired value for the new record in that column. Then call ContentResolver.insert() and pass it the URI of the provider and the ContentValues map. This method returns the full URI of the new record — that is, the provider's URI with the appended ID for the new record. You can then use this URI to query and get a Cursor over the new record, and to further modify the record. Here's an example:
要添加一个记录到内容提供者,首先要设置一个内容对象的键值对,每个键值匹配内容提供者的列的名,和那列新记录期望的值。然后调用方法,并传给该方法内容提供者的URI和内容值映像。这个方法,返回新记录的完全URI,即该新记录的带有追加ID的URI.然后,你可以用这个URI去查询并且让Cursor遍历整个对象,并进一步修改该对象,这里有一个例子:
import android.provider.Contacts.People;
import android.content.ContentResolver;
import android.content.ContentValues; 
 
ContentValues values = new ContentValues();
 
// Add Abraham Lincoln to contacts and make him a favorite.
values.put(People.NAME, "Abraham Lincoln");
// 1 = the new contact is added to favorites
// 0 = the new contact is not added to favorites
values.put(People.STARRED, 1);
 
Uri uri = getContentResolver().insert(People.CONTENT_URI, values);
Adding new values添加新值
Once a record exists, you can add new information to it or modify existing information. For example, the next step in the example above would be to add contact information — like a phone number or an IM or e-mail address — to the new entry.
当一个记录已经存在,你可以给他添加新的信息或者修改存在的信息。比如,在上面的例子的下一步,将添加一个新的联系信息-比如电话号,或者IM,或者e-mail地址-到该新的入点。
The best way to add to a record in the Contacts database is to append the name of the table where the new data goes to the URI for the record, then use the amended URI to add the new data values. Each Contacts table exposes a name for this purpose as a CONTENT_DIRECTORY constant. The following code continues the previous example by adding a phone number and e-mail address for the record just created:
在通信录数据库中增加一个新记录的最好方法是:追加一个表示名字,where the new data goes to the URI for the record,然后用改良的URI来添加新的数据值。每个通信录表为此目的解释成一个 CONTENT_DIRECTORY 常量。下面的例子,为一新创建的记录添加一个电话号和邮件地址。
Uri phoneUri = null;
Uri emailUri = null;
 
// Add a phone number for Abraham Lincoln. Begin with the URI for
// the new record just returned by insert(); it ends with the _ID
// of the new record, so we don't have to add the ID ourselves.
// Then append the designation for the phone table to this URI,
// and use the resulting URI to insert the phone number.
phoneUri = Uri.withAppendedPath(uri, People.Phones.CONTENT_DIRECTORY);
 
values.clear();
values.put(People.Phones.TYPE, People.Phones.TYPE_MOBILE);
values.put(People.Phones.NUMBER, "1233214567");
getContentResolver().insert(phoneUri, values);
 
// Now add an email address in the same way.
emailUri = Uri.withAppendedPath(uri, People.ContactMethods.CONTENT_DIRECTORY);
 
values.clear();
// ContactMethods.KIND is used to distinguish different kinds of
// contact methods, such as email, IM, etc. 
values.put(People.ContactMethods.KIND, Contacts.KIND_EMAIL);
values.put(People.ContactMethods.DATA, "test@example.com");
values.put(People.ContactMethods.TYPE, People.ContactMethods.TYPE_HOME);
getContentResolver().insert(emailUri, values);   
You can place small amounts of binary data into a table by calling the version of ContentValues.put() that takes a byte array. That would work for a small icon-like image or a short audio clip, for example. However, if you have a large amount of binary data to add, such as a photograph or a complete song, put a content: URI for the data in the table and call ContentResolver.openOutputStream() with the file's URI. (That causes the content provider to store the data in a file and record the file path in a hidden field of the record.)
你可以调用ContentValues.put()方法带字节数组的版本放置少量2进制数据。比如,可能是图像的小icon,或者简短的音乐剪辑。但是,你若加大量的二进制数据,比如图形或者完整的歌曲,放入一个内容:放数据的URI在表格中,调用使用文件的URIContentResolver.openOutputStream()方法,(这将导致内容提供者将数据存在一个文件中,并且在记录的隐藏域记录文件的路径)
In this regard, the MediaStore content provider, the main provider that dispenses image, audio, and video data, employs a special convention: The same URI that is used with query() or managedQuery() to get meta-information about the binary data (such as, the caption of a photograph or the date it was taken) is used with openInputStream() to get the data itself. Similarly, the same URI that is used with insert() to put meta-information into a MediaStore record is used with openOutputStream() to place the binary data there. The following code snippet illustrates this convention:  
就这一点而言,MediaStore内容提供者,分发图像,音频和视频数据的主要内容提供者,使用一个特别的惯例: query()方法或者managedQuery()方法使用同一个URI来获取关于二进制数据的信息(比如:图片标题,或者它的产生日期),它常用openInputStream()方法获取数据的.同样,该同一个URI也用于insert()插入信息到MediaStore记录,而它通常用于openOutputStream()方法把数据放到那里。
import android.provider.MediaStore.Images.Media;
import android.content.ContentValues;
import java.io.OutputStream;
 
// Save the name and description of an image in a ContentValues map. 
ContentValues values = new ContentValues(3);
values.put(Media.DISPLAY_NAME, "road_trip_1");
values.put(Media.DESCRIPTION, "Day 1, trip to Los Angeles");
values.put(Media.MIME_TYPE, "image/jpeg");
 
// Add a new record without the bitmap, but with the values just set.
// insert() returns the URI of the new record.
Uri uri = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, values);
 
// Now get a handle to the file for that record, and save the data into it.
// Here, sourceBitmap is a Bitmap object representing the file to save to the database.
try {
    OutputStream outStream = getContentResolver().openOutputStream(uri);
    sourceBitmap.compress(Bitmap.CompressFormat.JPEG, 50, outStream);
    outStream.close();
} catch (Exception e) {
    Log.e(TAG, "exception while writing image", e);
}
Batch updating records批更新数据
To batch update a group of records (for example, to change "NY" to "New York" in all fields), call the ContentResolver.update() method with the columns and values to change. 经批处理一组记录(比如,将”NY”改成”New York”到所有的域),调用ContentResolver.update()方法,带上列及要改变的值。
Deleting a record删除一个记录
To delete a single record, call {ContentResolver.delete() with the URI of a specific row. 删除一行,用ContentResolver.delete()方法,带上一个包括指定行的URI
To delete multiple rows, call ContentResolver.delete() with the URI of the type of record to delete (for example, android.provider.Contacts.People.CONTENT_URI) and an SQL WHERE clause defining which rows to delete. (Caution: Be sure to include a valid WHERE clause if you're deleting a general type, or you risk deleting more records than you intended!).
要删除多行,调用ContentResolver.delete()方法,带上一个要删除的记录的类型,和定义那些行的SQL WHERE簇URI(注意:如果你要删除一个通用的类型,确保包括了一个正确的WHERE簇,否则,你可能删除的记录,比你想要删除的记录多)
Creating a Content Provider创建一个内容提供者
To create a content provider, you must: 要创建一个内容提供者,你必须:
·         Set up a system for storing the data. Most content providers store their data using Android's file storage methods or SQLite databases, but you can store your data any way you want. Android provides the SQLiteOpenHelper class to help you create a database and SQLiteDatabase to manage it. 设置存储数据的一个系统,大多数内容提供者使用Android的文件存储数据, 或者SQLite数据库系统.但是你可以以你想要的任何方法存储数据。Android提供了一个SQLiteOpenHelper类,帮助你创建一个数据库,并且用SQLiteDatabase管理它.
·         Extend the ContentProvider class to provide access to the data.继ContentProvider承类,来提供数据访问。
·         Declare the content provider in the manifest file for your application (AndroidManifest.xml).在你的manifest文件中声明内容提供者(AndroidManifest.xml)
The following sections have notes on the last two of these tasks. 下面的部分,是关于这些任务的最后两项要注意的情况.
Extending the ContentProvider class继承一个内容提供者类。
You define a ContentProvider subclass to expose your data to others using the conventions expected by ContentResolver and Cursor objects. Principally, this means implementing six abstract methods declared in the ContentProvider class:
你可以定义类的子类,用内容解释者和Cursor对象所期望的约定,将数据解释给其他使用者。原则上,这意味着你要实现ContentProvider 类的,6个抽象方法。
query() insert() update() delete() getType() onCreate()
The query() method must return a Cursor object that can iterate over the requested data. Cursor itself is an interface, but Android provides some ready-made Cursor objects that you can use. For example, SQLiteCursor can iterate over data stored in an SQLite database. You get the Cursor object by calling any of the SQLiteDatabase class's query() methods. There are other Cursor implementations — such as MatrixCursor — for data not stored in a database.
query()方法,必须返回一个Cursor对象,并且它可以迭代请求的数据。Cursor本身是一个接口.但是Android系统提供一些准备好的 Cursor对象给你使用.比如,SQLiteCursor可以迭代存在于SQLite数据库中的数据。你可以调用任何一个SQLiteDatabase类的query()方法,得到一个Cursor对象。还有一些其他的Cursor实现,—比如MatrixCursor—用于非数据库存储的数据。
Because these ContentProvider methods can be called from various ContentResolver objects in different processes and threads, they must be implemented in a thread-safe manner.
由于这些内空提供者方法,可以从各种内容解释在不同的进程和线程调用,它们必须以线程安全的方式实现。
As a courtesy, you might also want to call ContentResolver.notifyChange() to notify listeners when there are modifications to the data. 出于好意,你可能也想调用ContentResolver.notifyChange()方法,通知监听器数据什么时候被改变了。
Beyond defining the subclass itself, there are other steps you should take to simplify the work of clients and make the class more accessible: 除了定义子类本身,还有其他一些指定客户端的工作步骤,以使得你的类更容易访问。
·         Define a public static final Uri named CONTENT_URI. This is the string that represents the full content: URI that your content provider handles. You must define a unique string for this value. The best solution is to use the fully-qualified class name of the content provider (made lowercase). So, for example, the URI for a TransportationProvider class could be defined as follows:
·         定义一个叫CONTENT_URI的公共静态的最终的Uri.这个字串表示你的内容提供者所能处理的所有content:URI.你必须为这个值指定一个唯一的字串,最好的解决方法是用内容提供者的全类名(由小写构成),所有,比如,TransportationProvider的URI定义成下面的样子:
·         public static final Uri CONTENT_URI = 
               Uri.parse("content://com.example.codelab.transportationprovider");
If the provider has subtables, also define CONTENT_URI constants for each of the subtables. These URIs should all have the same authority (since that identifies the content provider), and be distinguished only by their paths. For example:  
如果该内容提供者有子集,也要为每个子集定义CONTENT_URI常量.这些URIs应都有同样的认证(因为它标识内容提供者),并且只根据它们的路径区分,比如:
content://com.example.codelab.transportationprovider/train content://com.example.codelab.transportationprovider/air/domestic content://com.example.codelab.transportationprovider/air/international
For an overview of content: URIs, see the Content URI Summary at the end of this document. 想要概览content:URIs,请看本文档后面的Content URI Summary.
·         Define the column names that the content provider will return to clients. If you are using an underlying database, these column names are typically identical to the SQL database column names they represent. Also define public static String constants that clients can use to specify the columns in queries and other instructions.
·         定义内容提供者要返回给客户端的列名。如要使用一个基础数据库,这些列名,通常表示SQL数据库的列名。也要定义成public static字串常量,客户端可以在查询或者使用其他指令时,可以用它指定列名。
Be sure to include an integer column named "_id" (with the constant _ID) for the IDs of the records. You should have this field whether or not you have another field (such as a URL) that is also unique among all records. If you're using the SQLite database, the _ID field should be the following type:
确保包括了一个名字为”_id”的整型列,用于记录的IDs.无论你是否捅有了其他可以唯一标识所有记录的域,你也应捅有这个域。如果你使用SQLite数据库,_ID域应是这样的类型。
INTEGER PRIMARY KEY AUTOINCREMENT [整型,主健,自动增加]
The AUTOINCREMENT descriptor is optional. But without it, SQLite increments an ID counter field to the next number above the largest existing number in the column. If you delete the last row, the next row added will have the same ID as the deleted row. AUTOINCREMENT avoids this by having SQLite increment to the next largest value whether deleted or not.
AUTOINCREMENT的描述是可选的。但是如果不带上该描述,SQLite将在该列存在的最大数值之上,增加一个ID计数。如果我删除最后一行,下一个增加的行将与删除的行ID是相同的。AUTOINCREMENT是为避免这种情况,无论是否删除,都在下一个最大值上增加.
·         Carefully document the data type of each column. Clients need this information to read the data.每列数据类型的详细文件,客户端需要这些信息读取数据
·         If you are handling a new data type, you must define a new MIME type to return in your implementation of ContentProvider.getType(). The type depends in part on whether or not the content: URI submitted to getType() limits the request to a specific record. There's one form of the MIME type for a single record and another for multiple records. Use the Uri methods to help determine what is being requested. Here is the general format for each type:
·         如果你处理一新的数据类型 ,你必须在你的ContentProvider.getType()实现中,返回定义的新MIME类型 。
o    For a single record:    vnd.android.cursor.item/vnd.yourcompanyname.contenttype
For example, a request for train record 122, like this URI,
content://com.example.transportationprovider/trains/122
might return this MIME type:
vnd.android.cursor.item/vnd.example.rail
o    For multiple records:    vnd.android.cursor.dir/vnd.yourcompanyname.contenttype
For example, a request for all train records, like the following URI,
content://com.example.transportationprovider/trains
might return this MIME type:
vnd.android.cursor.dir/vnd.example.rail
·         If you are exposing byte data that's too big to put in the table itself — such as a large bitmap file — the field that exposes the data to clients should actually contain a content: URI string. This is the field that gives clients access to the data file. The record should also have another field, named "_data" that lists the exact file path on the device for that file. This field is not intended to be read by the client, but by the ContentResolver. The client will call ContentResolver.openInputStream() on the user-facing field holding the URI for the item. The ContentResolver will request the "_data" field for that record, and because it has higher permissions than a client, it should be able to access that file directly and return a read wrapper for the file to the client.
如果你将解释的数据太大,不能存在表中——比如一个大bitmap文件——解释数据给客户端的域应包括一个content: URI字串,这个域给客户访问数据文件。这个记录也应有另一个域,叫做”_data”,它列出了数据文件在设备上的确切路径。这个域不是给客户端读取的,而是为内容解释者提供的。客户端将调用持有该项URI的面向用户的ContentResolver.openInputStream()方法。ContentResolver将请求“_data”域。由于它的权限比客户端高,它应能直接访问文件,并且返回一个文件的包裹给客户端.
For an example of a private content provider implementation, see the NodePadProvider class in the Notepad sample application that ships with the SDK.
关于内容提供者实现的例子,请看NodePadProvider类,在NotePand例子应用中,SDK带有。
Declaring the content provider声明内容提供者
To let the Android system know about the content provider you've developed, declare it with a <provider> element in the application's AndroidManifest.xml file. Content providers that are not declared in the manifest are not visible to the Android system
为了让系统知道你开发的内容提供者,在应用的AndroidManifest.xml文件中用<provider>元素声明它。没有在manifest文件中声明的内容提供者,对Android系统不可见。
The name attribute is the fully qualified name of the ContentProvider subclass. The authorities attribute is the authority part of the content: URI that identifies the provider. For example if the ContentProvider subclass is AutoInfoProvider, the <provider> element might look like this:
name属性是内容提供者子类的全限定名。Authorities属性,是指定内容提供者的content:URI的部分认证。比如,如查ContentProvider子类是AutoInfoProvider,它的<provider>元素,可能是这样的:
<provider android:name="com.example.autos.AutoInfoProvider"
          android:authorities="com.example.autos.autoinfoprovider" 
          . . . />
</provider>
Note that the authorities attribute omits the path part of a content: URI. For example, if AutoInfoProvider controlled subtables for different types of autos or different manufacturers,
注意,authorities属性省略了content: URI的部分路径。比如,AutoInfoProvider控制不同汽车类型和不同厂商的子表
content://com.example.autos.autoinfoprovider/honda content://com.example.autos.autoinfoprovider/gm/compact content://com.example.autos.autoinfoprovider/gm/suv
those paths would not be declared in the manifest. The authority is what identifies the provider, not the path; your provider can interpret the path part of the URI in any way you choose.
这些路么不应用manifest中声明。该认证是标识内容提供者,不是路径。你的内容提供者可以以你选择的任意方式翻译URI的部分路径
Other <provider> attributes can set permissions to read and write data, provide for an icon and text that can be displayed to users, enable and disable the provider, and so on. Set the multiprocess attribute to "true" if data does not need to be synchronized between multiple running versions of the content provider. This permits an instance of the provider to be created in each client process, eliminating the need to perform IPC.
其他<provider>属性,可以设置读取和写入数据的权限,给用户提供一个图标及文本用来显示,允许及禁止内容提供者等。如果数据不需要在多个运行的内容提供者之间同步,将multiprocess属性设为true.它允许在每个客户端进程中创建一个内容提供者的实例,消除了执行IPC的需要.
Content URI Summary内容URI小结
Here is a recap of the important parts of a content URI: 下面是内容URI的重要概述
A. Standard prefix indicating that the data is controlled by a content provider. It's never modified. 一个内容前缀,指定由内容提供者控制的数据,它永不会修改
B. The authority part of the URI; it identifies the content provider. For third-party applications, this should be a fully-qualified class name (reduced to lowercase) to ensure uniqueness. The authority is declared in the <provider> element's authorities attribute:
   URI 的认证部分,标识内容提供者,对于第三方应用,这应该是一个全类限定名(减致全小写)以确保唯一性。认证声明在<provider>元素的authorities属性中。
C. <provider android:name=".TransportationProvider"
D.           android:authorities="com.example.transportationprovider"
          . . . >
E. The path that the content provider uses to determine what kind of data is being requested. This can be zero or more segments long. If the content provider exposes only one type of data (only trains, for example), it can be absent. If the provider exposes several types, including subtypes, it can be several segments long — for example, "land/bus", "land/train", "sea/ship", and "sea/submarine" to give four possibilities.
路径是内容提供者用于确定那种数据类型被请求的。这个可以是0,或者更长的段值。如果内容提供者只解释一种数据类型(比如只有trains),它可以缺少。如果内容提供者解释多种类型。包括子类型,它可以是多个段长。比如,"land/bus", "land/train", "sea/ship", and "sea/submarine"提供了4种可能的值。
F. The ID of the specific record being requested, if any. This is the _ID value of the requested record. If the request is not limited to a single record, this segment and the trailing slash are omitted:
如果有的话,ID指定请求的记录,这就是请求记录的_ID值。如果请求没有限定单个记录,这个段和后缀斜线可以省略
content://com.example.transportationprovider/trains
 
Except as noted, this content is licensed under Apache 2.0. For details and restrictions, see the Content License.
Android 4.0 r1 - 15 Dec 2011 23:11