你可以定义一个ContentProvider 子类来暴露你的数据给其它使用符合ContentResolver 和游标Cursor 对象约定的应用程序。
理论上,这意味需要实现6 个ContentProvider 类的抽象方法:query()
insert()
update()
delete()
getType()
onCreate()
query()方法必须返回一个游标Cursor 对象可以用来遍历请求数据,游标本身是一个接口,但Android提供了一些现成的Cursor 对象给你使用。
例如,SQLiteCursor 可以用来遍历SQLite 数据库。
你可以通过调用任意的SQLiteDatabase 类的query()方法得到它。
还有一些其它的游标实现-比如MatrixCursor-用来访问没有存放在数据库中的数据。
因为这些内容提供器的方法可以从不同的进程和线程的各个ContentResolver 对象中调用,所以它们必须以线程安全的方式来实现。
周到起见,当数据被修改时,你可能还需要调用ContentResolver.notifyChange()方法来通知侦听者。
除了定义子类以外,你应该还需要采取其它一些步骤来简化客户端的工作和让这个类更容易被访问:
• 定义一个public static final Uri 命名为CONTENT_URI。这是你的内容提供器处理的整个content: URI 的字符串。
你必须为它定义一个唯一的字符串。最佳方案是使用这个内容提供器的全称(fully qualified)类名(小写)。
因此,例如,一个TransportationProvider 类可以定义如下:public static final Uri CONTENT_URI =
Uri.parse("content://com.example.codelab.transporationprovider");
如果这个内容提供器有子表,那么为每个子表也都定义CONTENT_URI 常量。
这些URIs 应该全部拥有相同的权限(既然这用来识别内容提供器),只能通过它们的路径加以区分。例如:content://com.example.codelab.transporationprovider/train
content://com.example.codelab.transporationprovider/air/domestic
content://com.example.codelab.transporationprovider/air/international
定义内容提供器返回给客户端的列名。如果你正在使用一个底层数据库,这些列名通常和SQL 数据库列名一致。
同样还需要定义公共的静态字符串常量用来指定查询语句以及其它指令中的列。
确保包含一个名为"_id"(常量_ID)的整数列来返回记录的IDs。你应该有这个字段而不管有没有其它字段(比如URL),这个字段在所有的记录中是唯一的。
如果你在使用SQLite 数据库,这个_ID 字段应该是下面的类型:
INTEGER PRIMARY KEY AUTOINCREMENT
其中AUTOINCREMENT 描述符是可选的。
但是没有它,SQLite 的ID 数值字段会在列中已存在的最大数值的基础上增加到下一个数字。
如果你删除了最后的行,那么下一个新加的行会和这个删除的行有相同的ID。
AUTOINCREMENT 可以避免这种情况,它让SQLite 总是增加到下一个最大的值而不管有没有删除。
• 在文档中谨慎的描述每个列的数据类型。客户端需要这些信息来读取数据。
• 如果你正在处理一个新的数据类型,你必须定义一个新的MIME 类型在你的
ContentProvider.getType()实现里返回。这个类型部分依赖于提交给getType()的content: URI 参数是否对这个请求限制了特定的记录。
有一个MIME 类型是给单个记录用的,另外一个给多记录用。
使用Uri 方法来帮助判断哪个是正在被请求的。下面是每个类型的一般格式:
对于单个记录: vnd.android.cursor.item/vnd.yourcompanyname.contenttype 比如,一个火车记录122 的请求,URI 如下content://com.example.transportationprovider/trains/122 可能会返回这个MIME 类型:
vnd.android.cursor.item/vnd.example.rail
对于多个记录: vnd.android.cursor.dir/vnd.yourcompanyname.contenttype 比如, 一个所有火车记录的请求,URI 如下content://com.example.transportationprovider/trains
可能会返回这个MIME 类型:vnd.android.cursor.dir/vnd.example.rail
• 如果你想暴露过于庞大而无法放在表格里的字节数据-比如一个大的位图文件-这个给客户端暴露数据的字段事实上应该包含一个content: URI 字符串。
这个字段给了客户端数据访问接口。
这个记录应该有另外的一个字段,名为"_data",列出了这个文件在设备上的准确路径。
这个字段不能被客户端读取,而要通过ContentResolver。客户端将在这个包含URI 的用户侧字段上调用ContentResolver.openInputStream() 方法。
ContentResolver 会请求那个记录的"_data"字段,而且因为它有比客户端更高的许可权,它应该能够直接访问那个文件并返回给客户端一个包装的文件读取接口。
自定义内容提供器的实现的一个例子,参见SDK 附带的Notepad 例程中的NodePadProvider 类。