虽然在第3章已经就客户端如何使用内容提供者进行了简单介绍,在这里我们仍将详细介绍实现提供者的公用API的过程。对于要使用你实现的内容提供者的客户端,需要创建一个公用API类,它包含一组常量,用户可以使用这些常量来访问提供者的查询方法所返回的Cursor对象的字段。该类还定义了内容提供者的授权URI(authority URI),它是整个提供者URI通信机制的基础。在本节中,FinchVideo.SimpleVideos类提供了使用SimpleFinchVideo的API。
下面将逐步剖析这个类,对其各个字段进行说明,然后再介绍整个列表。
定义CONTENT_URI
客户端应用要查询内容提供者数据时,需要传递一个URI,用以标识Android内容解析程序要访问的数据。这些方法包括query、insert、update和delete,与之对应的是在P330“编写并集成内容提供者”一节中定义的内容解析程序的方法。在接收这种调用时,内容解析程序会使用authority字符串匹配输入的URI,每个内容提供者的CONTENT_URI会为客户端找到正确的提供者。因此,CONTENT_URI定义了内容提供者可以处理的URI类型。
一条CONTENT_URI包含以下部分:
content://
该前缀告诉Android框架,让其查找一个内容提供者来解析这个URI。
authority
该字符串唯一标识内容提供者,包含两个部分:组织部分和提供者标识部分。组织部分唯一标识创建该内容提供者的组织,提供者部分标识了这个特定的内容提供者。集成在Android中的内容提供者,略去了组织部分。Android中内置的media authority,返回的是没有包含authority的组织部分的一个或多个图像。任何非Google的Android团队所开发的内容提供者都必须同时定义内容提供者的两个部分。因此,对于简单的Finch视频示例应用,其authority是com.oreilly.demo.pa.finchvideo.SimpleFinchVideo,其中组织部分是com.oreilly.demo.pa.finchvideo,provider标识是SimpleFinchVideo。Google文档建议CONTENT_URI的authority部分最好是使用实现该内容提供者类的完整的限定修饰符。
Authority可以唯一标识出Android用以响应查询的专用的内容提供者。
path
内容提供者可以随意解释URI的其他部分,但是它必须遵循以下要求:
·如果内容提供者可以返回多种数据类型,则URI的path的某些部分必须能够指定要返回的数据类型。
·例如,内置的内容提供者“联系人”提供了很多种数据类型:people、phones、contact methods等。联系人内容提供者使用URI的字符串来区分用户使用了哪一种数据类型。因此,请求特定person的URI如下所示:
content://contacts/people/1
·请求特定电话号码的URI如下所示:
content://contacts/people/1/phone/3
·在第一个例子中,返回的MIME数据类型会是vnd.android.cursor.item/person,而第二个例子的MIME数据类型是vnd.android.cursor.item/phone。
·内容提供者必须能够返回单个或一组标识符。当某个标识符出现在URI的最后部分时,内容提供者会返回一项。回顾之前的示例,URI content://contacts/people/1/phone/3返回一个电话号码,其类型是vnd.android.cursor.item/phone。如果URI的类型是content://contacts/people/1/phone,则应用会返回标识符为1号的人的所有电话号码,返回的数据的MIME类型则变为vnd.android.cursor.dir/phone。
正如前面所述,内容提供者可以解释URI的路径部分以满足需求。这意味着路径部分可以使用路径中的项过滤返回给调用函数的数据。例如,内建的“媒体”内容提供者可以返回内部数据或外部数据,它取决于路径中的URI中包含的是internal还是external。
简单Finch视频的完整CONTENT_URI是:content://com.oreilly.demo.pa.finchvideo.SimpleFinchVideo/video。
CONTENT_URI必须是public static final类型的。它在简单视频应用的Finch Video类中定义。在公共API类中,可以扩展类的BaseColumns,然后定义名为AUTHORITY的字符串:
public final class FinchVideo.SimpleVideos extends BaseColumns { public static final String SIMPLE_AUTHORITY = /"com.oreilly.demo.pa.finchvideo.FinchVideo/";
然后,定义自己的CONTENT_URI:
public static final class FinchVideo.SimpleVideos implements BaseColumns { public static final Uri CONTENT_URI = Uri.parse(/"content:///" + AUTHORITY + /"/video/");
更简单地说,定义该URI只需要选取一个authority字符串,它应该使用应用中所使用的Java包作为组织标识符——使用公共的API包可能比从头实现一个包要好一些,正如我们在P58“Java包”一节中所讨论的。内容提供者标识符只是内容提供者类的名称。简单Finch视频提供者的URI看起来如下所示:
/"content:///" + FinchVideo.FinchVideoContentProvider.SIMPLE_AUTHORITY + /"//" + FinchVideo.SimpleVideos.VIDEO
创建字段名称
内容提供者和客户端交换数据的方式类似于SQL数据库和数据库应用之间的交换方式:使用能够表示整条记录及其字段数据的游标。内容提供者必须定义其支持的字段名称,正如数据库应用必须定义其支持的字段名称。当内容提供者使用SQLite数据库作为数据存储时,最常见的解决方案是让内容提供者的字段和数据库的字段保持一致,SimpleFinchVideoContentProvider采用的就是这种方式。因此,SimpleFinchVideoContentProvider字段和底层的数据库字段之间就不需要额外的映射了。
注意:有些应用不希望它的全部数据都能够被所有客户端的内容提供者所访问,有些复杂的应用可能希望客户端访问其派生出的数据视图。P335“.SimpleFinch VideoContent Provider类和实例变量”一节中描述的映射方式可以用于处理这种复杂的情况。
声明字段规范字符串
本节要探讨的FinchVideo.SimpleVideos类定义了字段SimpleFinchVideoProvider。每个内容提供者必须定义_id字段来保存每条记录的记录号。每个_id值在内容提供者范围内必须是唯一的。当客户端想要查询一条记录时,会把这个记录的_id传递给内容提供者的vnd.android.cursor.item URI。
如果内容提供者的数据与SimpleFinchVideoProvider一样也是保存在SQLite数据库中,那么其_id字段的类型应该是INTEGER PRIMARY KEY AUTOINCREMENT。在这种方式中,记录有唯一的_id号,而且该_id号不会被重用,即使该条记录被删除了。通过确保每条记录都有一个_id号而且这个_id号不会被重用,可以支持参考完整性。如果记录的_id号被重用,就可能会导致缓存的URI指向了错误的数据。
以下是简单的Finch视频提供者API,即FinchVideo.SimpleVideos类的完整代码。注意,这里只给出了需要的常量。没有定义内容提供者实现的所有常量,因为它们对于客户端是没有用的,可能会导致客户端使用特定的内容提供者实现。我们致力于良好的软件设计,确保软件层是独立的,客户端不应该直接依赖内容提供者的实现。Finch视频提供者API的完整代码如下:
/** * Simple Videos columns */public class FinchVideo { public static final class SimpleVideos implements BaseColumns { // This class cannot be instantiated private SimpleVideos {} // uri references all videos public static final Uri VIDEOS_URI = Uri.parse(/"content:///" + SIMPLE_AUTHORITY + /"//" + SimpleVideos.VIDEO); /** * The content:// style URL for this table */ public static final Uri CONTENT_URI = VIDEOS_URI;① /** * The MIME type of {@link #CONTENT_URI} providing a directory of notes. */ public static final String CONTENT_TYPE = /"vnd.android.cursor.dir/vnd.finch.video/";② /** * The MIME type of a {@link #CONTENT_URI} sub-directory of a single * video. */ public static final String CONTENT_VIDEO_TYPE = /"vnd.android.cursor.item/vnd.finch.video/";③ /** * The video itself * <P>Type: TEXT</P> */ public static final String VIDEO = /"video/"; /** * Column name for the title of the video * <P>Type: TEXT</P> */ public static final String TITLE = /"title/"; /** * Column name for the description of the video. */ public static final String DESCRIPTION = /"description/"; /** * Column name for the media uri */ public static final String URI = /"uri/"; /** * Unique identifier for an element of media */ public static final String MEDIA_ID = /"media_id/"; }...// The API for FinchVideo.Videos is also defined in this class.}
下面是这段代码的几个重点:
① 使用VIDEOS_URI定义CONTENT_URI的值。视频URI包含所描述的内容URI。
② 这是提供者存储的video项的MIME类型。P337“实现getType方法”一节将解释内容提供者如何使用这个MIME类型。
③ 这些是客户端用来访问提供者创建的Cursor对象的字段名称。