ContentProvider实现举例

  ContentProvider将数据封装起来,对外提供统一的接口,使得对数据的访问独立于数据的存储方式(文件、数据库等)。ContentProvider还提供跨应用的数据访问能力,并可以通过Search Framework实现自定义的搜索建议。可以参考这里来决定是否使用ContentProvider。

  下面例子在RecyclerView使用方法举例(1)的基础上,展示ContentProvider的一个简单实现。

1. 设计数据存储方式

  ContentProvider是访问数据的接口,首先要决定数据的具体存储方式。这里使用SQLite存储从TMDb上取得的一系列电影信息。

1.1. 设计Contract

  Contract定义了数据的具体结构,以及用于访问数据的URI的结构。参考这里的从TMDb取得JSON文件样例,电影信息应包含如下几个字段:

  定义Provider的名称,即Authority:

  在此基础上,定义URI的格式:

  定义Content Type:

这里 CURSOR_DIR_BASE_TYPE 指示URI包含的Cursor具有零个或多个元素, CONTENT_ITEM_TYPE 指示URI包含的Cursor只具有一个元素。

【完整代码】

1.2. 实现数据库存储

  Android提供了SQLiteOpenHelper用于数据库的创建和版本管理,创建其子类来实现电影数据库的创建:

onCreate() 中根据Contract中的定义,构造创建数据库的SQL语句并执行,由此创建数据库。 DATABASE_VERSION 指示数据库版本, onUpgrade() 用来处理数据库版本升级,这里只是将数据库删除重新创建,因为例子中使用的数据都可以从TMDb获取。

【完整代码】

1.3. 实现Content Provider

  实现Content Provider首先要继承ContentProvider,主要工作是实现 query() 、 insert() 、 update() 、 delete() 四个方法。另外还要实现一个UriMatcher,用于获取访问数据的URI的类型,根据不同的URI类型,前面的四个方法可能需要进行不同的处理。

1.3.1. 实现UriMatcher

  首先定义URI类型,这里只有两种:

MOVIE 类型对应 content://com.nex3z.examples.contentprovider/movie ,用于获取所有电影数据; MOVIE_WITH_ID 类型对应形如 content://com.nex3z.examples.contentprovider/movie/# 的URI,这里的 # 表示任意数字,用于获取id为 # 的一个电影。此外还可以用 * 表示任意有效字符,这里没有使用。

  然后实现UriMatcher:

1.3.2. 实现query()

  query()用于请求数据。针对 MOVIE_WITH_ID 类型的URI,只向数据库请求指定id的电影;针对 MOVIE 类型的URI,向数据库请求所有电影。

  这里 getMovieById() 实现如下:

其中 sMovieIdSelection 和 sMovieQueryBuilder 分别为:

这里 sMovieIdSelection 用问号?代替了参数, sMovieQueryBuilder.query() 通过分别指定 selection 和 selectionArgs 分离了查询和参数,可以防止数据库注入。

1.3.3. 实现insert()

  insert()用于插入数据,向数据库插入数据后,使用 notifyChange() 通知数据变化,生成对应的URI并返回:

1.3.4. 实现update()

  update()用于更新已有数据,成功更新后,使用 notifyChange() 通知数据变化:

1.3.5. 实现delete()

  delete()用于删除数据,成功删除后,同样使用 notifyChange() 通知数据变化:

1.3.6. 实现bulkInsert()

  如果数据条目较多,单独使用insert()效率较低,bulkInsert()提供了打包插入数据的方法,更加高效。

这里先使用 beginTransaction() 以EXCLUSIVE模式开始一个Transaction,然后依次插入数据,此时数据并没有被真正插入。之后调用 setTransactionSuccessful() 标记当前Transaction为成功,最后通过 endTransaction() 应用修改。

【完整代码】

2. 使用ContentProvider

2.1. 声明Provider

  使用ContentProvider前,首先要在AndroidManifest.xml中声明所使用的Provider:

【完整代码】

2.2. 插入数据

  从Retorfit获取到Movie后,首先转换为ContentValue,然后就可以通过 bulkInsert() 插入到数据库了。

这里 getContentResolver() 会根据所使用的URI获取对应的Content Provider,不需要直接指定Content Provider。

【完整代码】

2.3. 请求数据

  向Content Provider请求数据的方法可以参考使用CursorLoader及更新RecyclerView

3. 完整代码

  完整代码可以参考这里