android Content Provider详解六

创建一个Content Provider

content provider管理对中央数据仓库的存取。你实现一个provider,就是在一个Android应用中实现一个或多个类,再加上manifest文件中的一些元素。你实现一个 ContentProvider的子类,它作为你的provider和其它应也之间的接口。尽管content providers的目的是向其它应用提供数据,但当然也可以在你自己的应用中创建activity来允许用户来查询和修改你的provider所管理的数据。

创建之前的准备工作

在创建一个provider之前,需做以下工作:
1. 确定你是否需要一个content provider。你如果需要提供一个或多个下列特性,你就需创建一个content provider:
你想向其它应用提供复杂数据或文件。
你想让用护从你的应用复制复杂的数据到其它应用。
你想使用搜索框架提供自定义的搜索建议。
如果完全是在你的应用内部使用,你的provider不需使用 SQLite数据库。
2. 如果你还未作出决定,请阅读主题 Content Provider Basics 来进一步了解provider。
下一步,按以下步骤来创建你的provider:
1. 为你的数据设计原始存储方式。一个content provider以两种方式提供数据:
文件数据
指那存储在文件中的数据,比如图片,视频,音频等。把文件们存储在你的应用的私有空间。为响应从其它应用发来的对某个文件的请求,你的provider可以为文件提供一个句柄。
"结构化" 数据
指那些存储于数据库中的数据、数组或小型结构。数据以兼容于表的形式存储。一行代表一条数据,就像一个人员或条目清单中的一条。一列代表一条数据中的一部分数据,比如人的名字或条目的价格。存储这些类型的数据的一个常用方法是使用SQLite数据库,但是你也可以使用其它形式。要了解android 系统中更多的存储方式,见 设计数据存储一节。
2. 具体定义ContentProvider 类和它的方法。此类是你的数据与Android系统中其它东西的接口。要进一步了解此类,见实现ContentProvider类 一节。
3. 定义provider的authority字符串,content URIs,和列的名字。如果你想让provider的应用处理intent,还要定义intent 的action、附加数据和标志。还要定义对那些要访问你的数据的应用所需具有的权限。你应该考虑把这些值作为契约定义到另外一个单独的契约类中。以后就可以向其它的开发者展示这个类。更多关于content URI的信息,见 设计Content URI.一节。更多关于intent的信息,见Intent和数据操作一节。
4. 添加其它可选内容,比如样本数据或实现 AbstractThreadedSyncAdapter 以实现provider和基于云的数据之间的数据同步。

定义数据存储

一个content provider是一个结构化存储的数据接口。在你创建这个接口之前,你必须确定如如何存储数据。你可以以任何你喜欢的方式存储数据,然后设计读写数据的接口。
下面是一些在android中可用的数据存储技术:
1 Android系统中包含一个 SQLite数据库API,Android自己的provider使用它来存储表格类的数据。SQLiteOpenHelper类帮助你创建数据库,而类SQLiteDatabase是操作数据库的基础类。
记住你不是必须使用一个数据库来实现你的数据仓库。一个 provider在外部的表现就像一个表的集合,类似于一个关系型数据库,但是provider的内部实现是可以与此不同的。
2 对于数据存储,Android具有各种各样的面向文件的API。要更多的了解文件存储,请阅读主题数据存储。如果你 设计的provider要提供媒体相关的数据,比如音频和视频,你可以在provider中将表数据和文件混合来使用。
3 要处理网络数据,应使用java.net 和 android.net中的类。你也可以同步网络数据到本地数据存储中(比如数据库中),然后以表或文件的形式提供此数据。例子Sample Sync Adapter 演示了这种同步技术。


思考数据设计

下面是一些设计你的provider的数据结构的技巧:
1 表数据应总是具有一个"主键"列,它被用于管理一个代表每各行的唯一数值。你可以使用这个值链接某行到其它表中的相关行 (也就是"外键")。尽管你可以为此列取任何名字,但使用BaseColumns._ID 才是最佳选择,因为当把一个provider的查询结果关联到一个 ListView 时,需要有一个列叫做 _ID。
2 如果你想提供位图图像或其它的非常大的面向文件的数据,那么应把它存于文件中,然后间接的提供它,而不是直接把数据存储于一个表中。如果你这样做了,你还需要告诉你的provider的用户,他们需要使用一个ContentResolver 的文件方法来操作数据。
3用大二进制对象 (BLOB)数据类型来存储大小不定或没有固定结构的数据。例如,你可以使用一个 BLOB列来存储 协议缓冲 或 JSON 结构。
你也可以使用一个BLOB来实现一个独立模式的表。在此种表中,你定义一个主键列,一个MIME类型列,和一个或多个普通的BLOB列。在 BLOB 列中的数据的意义由MIME列中的值所表明。这使你可以在一个表的各行中存储不同类型的数据。联系人Provider的"data"表ContactsContract.Data ,就是一个独立模式表的例子。


设计内容URIs

一个内容URI 标志一个provider中的数据。内容URI包含了整个provider (他的 authority)的符号名和一个指向某个表或文件的名字。可选的id部分指向表中的一个独立的行。ContentProvider 的每个数据操作方法具有一个内容URI作为一个参数;这允许你决定要操作的表,行或文件。
内容URI的基础在标题Content Provider基础 中被描述。


设计一个authority

一个provider通常具有单个authority,这作为它的安卓内部名字。为了避免与其它provider冲突,你应该使用互联网域名方式取名 (不过是倒着的)来作为你的provider authority的基础名字。因为此种建议也用于Android包的名字,所以你可以定义你的provider authority作为包含此provider的包名的扩展。 例如,如果你的Android包名是 com.example.<appname>,你应为你的provider authority命名为 com.example.<appname>.provider。


定义一个路径结构

开发者通常通过添加指向独立表的路径来从authority 创建content URI。例如,如果你具有两个表 table1和table2,你从前面例子中的authority 合并出来的内容 URI为 com.example.<appname>.provider/table1 和com.example.<appname>.provider/table2。路径中并不限制只有一个参数,并且路径的每一层也不是必须是一个表。


处理内容URI的 ID

为了方便,provider接受把一行的ID放在URI的尾部来提供对表中某一行的访问。同样为了方便,provider比较此 ID和表的_ID列,然后执行对匹配行的操作请求。.
这种方便性有助于在应用操作一个provider时使用一种通用的设计模式。应用向provider发出查询然后在一个ListView 中使用一个CursorAdapter 显示结果 Cursor 。CursorAdapter 的定义需要Cursor 中有一个列叫做 _ID 。
用户之后就可以在UI中显示的行中选择一行进行查看或修改数据。app之后从ListView 背后的Cursor 中获得相应的行,再获得行的 _ID值,把此值添加到内容URI上,再把操作请求发送给provider。provider之后就执行查询或修改用户所指定的行。


内容URI的模式

为了帮你跟据到达的内容URI 选择要执行的动作,provider API包含了简便的类UriMatcher,它映射内容URI 的"模式" 到一个整数值。你可以在一个switch 语句中使用这个整数值来选择为匹配一种模式的某个内容URI或多个URI们执行某种动作。
一个内容 URI使用通配符来匹配其它内容URI们:
*: 匹配任意长度的任意有效字符串。
#: 匹配由数字组成的任意长度的字符串。
看一个设计和编码处理content URI的例子,假设一个provider其authority是 com.example.app.provider ,从它可以识别以下指向各表的内容URI:
1content://com.example.app.provider/table1: 一个叫table1 的表。
2content://com.example.app.provider/table2/dataset1: 一个叫dataset1 的表。
3content://com.example.app.provider/table2/dataset2: 一个叫dataset2 的表。
4content://com.example.app.provider/table3: 一个叫做table3 的表。
provider也识别那些具有一行的ID的内容URI,例如 content://com.example.app.provider/table3/1 指向表table3中的行1.
下面的内容 URI 模式也可能出现:
content://com.example.app.provider/*
匹配provider 中的任务内容URI。
content://com.example.app.provider/table2/*:
匹配表dataset1和表dataset2中的一个内容URI,但是不匹配table1或table3中的内容URI。
content://com.example.app.provider/table3/#: 匹配表 table3 中的任意一行,比如content://com.example.app.provider/table3/6 指向行6.
下面的代码片段演示了UriMatcher 中的方法们如何工作。此代码通过使用内容URI模式content://<authority>/<path> 指向表,用content://<authority>/<path>/<id> 指向一个单独行,以不同的方式来处理指向整个表的 URI和指向单行的URI 。
方法addURI() 映射一个authority和路径到一个整数值。方法android.content.UriMatcher#match(Uri) match()} 返回一个URI对应的整数值。一个switch 语句从查询整个表和查询单条记录之间作出选择:

public class ExampleProvider extends ContentProvider {
...
  // 创建一个UriMatcher对象
  private static final UriMatcher sUriMatcher;
...
  /*
  * 到这里,是对addURI()的调用。provider应能识别所有的内容URI的模式。
  * 但在此片段中,只演示对表3的调用。
  */
...
  /*
  * 映射表3中的多行模式为1 。注意路径中没有用通配符。
  */
  sUriMatcher.addURI("com.example.app.provider", "table3", 1);
  /*
  * 映射单行模式为2 。 此例中,通配符"#"被使也。
  * "content://com.example.app.provider/table3/3" 能匹配此模式,但是
  * "content://com.example.app.provider/table3就不能了。
  */
  sUriMatcher.addURI("com.example.app.provider", "table3/#", 2);
...
  // 实现ContentProvider.query()
  public Cursor query(
    Uri uri,
    String[] projection,
    String selection,
    String[] selectionArgs,
    String sortOrder) {
...
    /*
    * 选择一个要查询的表并跟据为输入URI返回的代码来选择排序顺序。
    * 这里也是只演示表3 。
    */
    switch (sUriMatcher.match(uri)) {
      // 如果输入URI指向整个表3
      case 1:
        if (TextUtils.isEmpty(sortOrder)) sortOrder = "_ID ASC";
        break;
      // 如果输入URI指向单行
      case 2:
        /*
        * 因为这个URI指向单行,所以_ID部分就出现了。
        * 从URI获取最后最后面的路径段;它就是_ID值。
        * 然后,添加这个值到查询语句的WHERE子语句中。
        */
        selection = selection + "_ID = " uri.getLastPathSegment();
        break;
      default:
      ...
        // 如果URI不能被识别,你应该在此处做一些错误处理。
    }
    // 调用进行实际查询的代码
  }

另一个类,ContentUris, 提供处理内容URI中的id部分的简便方法。类Uri 和 Uri.Builder 包含了分析已存在的Uri 对象和创建瓯新对象的简便方法。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值