Android之Content Providers

烈酒焚心 提交于 2019-12-15 16:29:47

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

Content Providers

  Content Providers存储和取得数据,以及让它对所有应用程序可见。这是惟一的跨应用程序共享数据的方法;没有一个所有Android包都能访问的公共存储区域。

  Android整理了一大堆content provider给公共数据类型(音频、视频、图像、联系人信息等待)。你能看见他们中的一些在android.provider包中。你能查询 providers拥有的数据(虽然,有一些,你必须获取合适的权限来读取数据)。

  如果你希望做你自己的数公共数据,你有两个选项:你可以创建你自己的content provider(一个ContentProvider子类)或你可以添加数据到一个已存在的provider--如果有一个控制同样的数据类型的话并且你拥有写的权限。

Content Provider Basics

  一个content provider怎样实际存储它的数据由设计者决定。但所有的content provider实现一个公共的接口来查询和取得结果--同样的对添加、修改、删除数据。

  这是一个客户间接使用的接口,一般通过ContentResolver对象。你获取一个ContentResolver通过getContentResolver()从activity的实现或其它组件:

  

  你可以然后使用ContentResolver的方法来和你感兴趣的任何content provider交互。

  当一个查询被初始化,android系统标识被找准的content provider,保证它在运行。系统立马示例所有的ContentProvider对象;你永远不需要自己来做。实际上,你永远不会直接处理 ContentProvider。典型的,每个ContentProvider只有一个实例。但它可以和多个ContentResolver对象交互在不 同的应用程序和不同的进程 。进程间交互被ContentResolver和ContentProvider操作。

The data model

  Content providers暴露他们的数据作为简单的表在基于一个数据库模型,每行是一条记录,每一列是特定类型的数据。例如,关于人的信息和他们的手机可以暴露如下:

  每一条记录包含一个数字_ID域用来惟一标识记录在表中。IDs可以被用来匹配记录在相关表中--例如,为了找到一个人的电话号码在一个表以及在另一个表中的图。

  查询返回一个Cursor对象,可以移动从记录到记录并且列到列来读取每个域的内容。它有特殊的方法来读取每个类型的数据。所以,为了读一个域,你必须知道哪种类型的数据这个域包含。

URIs

  每一个content provider暴露一个公共的URI(Uri对象),用来识别数据集。一个content provider控制多个数据集(多个数据表)为每个暴露一个URI。所有的URIs以"content://"开始。"content:"方案标识数据 被content provider控制。

  如果你定义一个content provider,定义一个常量为它的URI是个好主意,为了简化客户端代码并且让以后升级更干净。Android定义CONTENT_URI常量为所有 的平台providers。例如,一个表的URI匹配某个人的电话并且这个表的URI拥有这个人的图片(都被联系人content provider控制):

  

  URI常量被用于content provider的交互。每个ContentResolver方法以URI作为第一个参数。它标致着哪个一providerContentresolver需要去交流并且哪一个表需要被指向。

Querying a Content Provider

  你需要三种信息来查询一个content provider:

  1、标识provider的URI。

  2、你希望接收的域名。

  3、这个域的数据类型。

  如果你查询某一个记录,你也需要那条记录的ID。

Making the query

  为了查询一个content provider,你可以使用ContentResolver.query()方法或Activity.managedQuery()方法。两个方法使用 相同的参数,两个都返回Cursor对象。可是,managedQuery()引起activity来管理 Cursor的生命周期。一个被管理的Cursor操作所有的细节,例如卸载它自己当activity停止,查询它自己当activity重启。你可以要 求一个Activity去管理一个未被管理的Cursor对象为你,通过Activity.startManagingCursor()。

  第一个给query()或managedQuery()的参数是provider的URI--自助CONTENT_URI常量标识一个特定的ContentProvider和数据集。

  为了限制一个查询只能一条记录,你可以添加_ID值给给那个URI--也就是说,放一直匹配的字符串到URI的最后的部分。例如,如果ID是23,那么 URI就会是:

  

  有一些帮助方法,特别是ContentUris.withAppendedId()和Uri.withAppendedPath(),使得添加 一个ID到URI很容易。两个静态方法都返回一个添加了ID的Uri对象。所以,例如,如果你在寻找23号记录在联系人数据库中,你可以构造一个查询像下 面这样:

  

  其它参数限制query()和managedQuery()方法到更细节。他们是:

  1、返回的数据列的名字。NULL值返回所有列。否则,只有被列出名字的值才会返回。所有和平台一起的content providers定义常量为他们自己列。例如,android.provider.Contacts.Phones类定义 常量为列名在phone表中就像之前陈述的&mdash _ID、NUMBER、NUMBER_KEY、NAME等等。

  2、一个跟行有关的过滤器,以SQL WHRER往往名(不包含WHERE itself)。一个NULL值返回所有行(除非URI限制单选查询)。

  3、选择参数。

  4、一个排序的行被返回,格式化像SQL ORDER从句(不包括ORDER BY itself)。一个NULL值返回原始记录顺序,也就是没有排序。

   让我们看一个查询示例,取得联系人名字和他们电话号码的列表:

  这个查询从联系人content provider的人员表中取得数据。它取得名字,电话号码,和惟一的记录ID。它也报告记录的数量作为被每条记录的_COUNT返回的。

  列名常量被定义在不同的接口--_ID和_COUNT在BaseColumns,NAME在PeopleColumns,以及NUMBER在phoneColumns。Contacts.People类实现每个接口。

What a query returns

  一个查询返回一系列的零或更多数据库记录。列表、默认序、数据类型对每个content provider来说都很特别。但每一个provider 有一个_ID列。每个provider也能将记录数返回在_COUNT列中;它的值和所有的行一样。

  下面是一个例子,展现之前的结果:

  

  取得的数据被暴露给一个Cursor对象可以向后或向前穿越结果集。你可以使用这个对象只读数据。添加、修改、删除数据,你必须使用一个ContentResolver对象。

Reading retrieved data

  通过查询获得的Cursor提供访问结果记录的功能。如果你已经查询一个特定记录通过ID,这个集合将包含只有一个值。否则,它可以包含多个 值。(如果没有匹配,它为空)你可以读取数据从指定域在记录中,但你必须知道域的数据类型,因为Cursor对象有一个单独的方法来读各种类型的数据-- 例如getString(),getInt()和getFloat()。(可是,对大多数类型,如果你调用 读取字符串的方法,Cursor对象将给你数据的字符串表示。)Cursor让你请求表列名用索引号,或用列名获取索引号。

  下面的版本陈述读取名字和电话号码从之前的查询:

  

  如果一个查询能返回二进制数据,例如图形或声音,数据 可以直接进入表或数据的表头可能是一个字符串“content:URI”,你能使用来获取数据。一般,比较小的数据(20-50K或更小)经常被直接插入 表中并表可以被Cursor.getBlob()读取。它返回一个二进制数组。

  如果表入口是“content:URI”,你应该永远不尝试打开和读取文件直接(其中一点是,权限会导致失败)。相反,你应该调用ContentResolver.openInputStream()来获取一个InputStream对象,你可以用来读取数据。

Modifying Data

  被content provider保存的数据可以被修改:

  1、添加一条新记录。

  2、添加新值到当前的记录。

  3、批量更新当前记录。

  4、删除记录。

  所有的数据修改都通过ContentResolver完成。一些content provider要求更严格的权限写数据。如果你没有权限写入一个content provider,ContentResolver会失败。

Adding records

  添加一条新记录给一个content provider,首先建立键-值对在ContentValues对象,每个key匹配列名在content provider,并且值是那个记录的那个列期望的值。然后调用ContentResolver.insert()并且传递它provider的URI和 ContentValues 地图。这个方法返回完成的新记录的URI--也就是,provider的新记录添加了ID的URI。你可以使用这个URI来查询和获得Cursor关于这 条新记录的,并且进一步修改数据。下面是一个例子:

  

Adding new values

  一旦记录建立,你可以添加新信息为它或修改已存在的信息。例如 ,下一步在上例中将添加联系人信息--像电话号码或IM或e-mail地址--给新的入口。

  最好的方式添加一条记录在联系人数据库是添加表的名字在URI后面,然后使用这个改动的URI来添加数据值。每个联系表导出一个名字为这个目录作为CONTENT_DIRECTORY常量。下面的代码继续之前的示例通过添加一个电话号码和邮件地址给新记录:

  

  你可以放置小量二进制数据到表中通过调用ContentValue.put()。它对像小图标或短音频起作用,例如。可是,如果你有一大量二进 制数据来添加,例如一个图片或一首完整歌曲,为数据放置"content:URI"在表中并且调用 ContentResolver.openOutputStream()用文件的URI。(这引起content provider存在数据在一个文件,然后记录文件地址在一个隐藏域。)

  在这点上,MediaStore content provider,这个主要的分发图像、音频、视频的provider,利用一个特别的协议:同样的使用query()和managedQuery()的 URI来获取meta-information关于二进制数据(例如,图像的标题或拍摄的日期)和openInputStream一起使用来获取它自身的 数据。类似的,和insert()使用的URI放置meta-information给一个MediaStore和openOutputStream的记 录来旋转二进制数据那里。下面的代码版本陈述了这个协议。

  

Batch updating records

  为了气量更新一组记录(例如,为了改变“NY”成“New York”在所有的域),调用ContentResolver.update()方法用列和值来改变。

Deleting a record

  为了删除单条记录,调用 ContentResolver.delete()用URI和指定的行。

  为了删除多条记录,调用 ContentResolver.delete()用记录类型的URI和来删除(例如,android.provider.Contacts.Peopple.CONTENT_URI)并且一个SQL WHERE从句有写哪行被删除。(警告:保证包含一个有效的WHERE如果你在删除一般类型,否则你将有删除比你希望的更多的数据)。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!