使用Content Provider为其他应用提供数据
Content providers can help an application manage access to data stored by itself, stored by other apps, and provide a way to share data with other apps.
一、定义
Google的文档定义非常清晰,Content Provider就是用来与其他进程共享数据的标准方式。例如,社交应用需要获取你的联系人数据,为你迅速找到朋友的账号,联系人数据就是从系统应用联系人提供的Content Provider中获取的。它的具体结构可以用下图来展示。

具体来说,数据存储在Sqlite或者其他持久化方案中,Content Provider对外暴露对这些持久化数据的CRUD接口,而其他应用则可以通过这些接口完成对数据的读写。
二、为应用创建Content Provider
下面就基于SQLite3数据库,创建一个可以为其他应用提供CRUD接口的简单应用,一步步地学习Content Provider。该应用将为外界提供一组NBA球员的数据,其他应用可以通过接口对这些数据进行读写。
2.1 创建Content Provider
使用Android Studio(我使用的是最新的4.1.2版本)可以轻易创建一个Content Provider,先创建一个应用,因为是单纯的数据提供应用,所以选择No Activity的就行。在包名目录下右键new->other->Content Provider,如下图所示。

在新的窗口中为Content Provider命名,这里我使用了NBAPlayersContentProvider,URI Authority这一栏用于填写对外暴露的URI接口,其他应用将使用这个值作为URI的host部分(暂时认为这是NBAPlayersContentProvider的ID吧),一般的惯用写法是:
这里我们由于只有一个provider,所以命名为com.rodneycheung.contentprovidersample.provider。Exported和Enabled都勾上,不勾也没事,反正在AndroidManifest.xml中也可以加上,然后点击Finish即可。

创建完成后,Android Studio会为我们自动生成代码如下:
2.2 Content Provider接口解释
Android Studio为我们自动生成的NBAPlayersContentProvider继承于ContentProvider,对于每个重写的函数都标注了函数的作用。我们一共需要实现一下几个,首先是onCreate方法,这个用于Content Provider创建的时候做的一些初始化工作;然后是getType,这个用于返回每个CRUD接口的MIME类型;最后就是对外暴露的CRUD接口,跟数据库中的很像,连参数都类似。query用于从查询数据,insert用于插入数据,update用于更新数据,delete用于删除数据。其实这些对于有数据库开发基础的开发者都很好理解。
2.3 实现数据持久化部分
前面的图解中说了,Content Provider中的数据来源于持久化方案中,所以我们先基于SQLite3把数据部分实现了。我们只需要创建一张表Players,表结构如下:
字段名称
类型
含义
name
text
球员姓名
score
real
场均得分
rebound
real
场均篮板
assists
real
场均助攻
block
real
场均盖帽
steals
real
场均抢断
下面创建数据库类NbaDbHelper
首先在onCreate方法中实现数据库的创建和升级逻辑:
然后我们添加一些供NBAPlayersContentProvider访问的数据读写接口。
至此,数据库部分就是先完成了。
2.4 完成Content Provider
2.4.1 onCreate
该函数在Content Provider创建时调用,一般我们会在这里初始化数据库。
2.4.2 getType
该函数用于返回内容Uri的MIME类型。先解释一下什么叫内容Uri,之前的NBAPlayersContentProvider方法中很多都带一个叫Uri的参数,这个就是所谓的内容Uri。一个标准的内容Uri的格式是:
authority就是前面创建NBAPlayersContentProvider时的值,path很好理解,用于定位NBAPlayersContentProvider中的资源。一般path的格式为:
*表示任意字符串,#表示任意数字。在这里,我们稍微简单一点,只用第一种格式,即可以在整张表中通过SQL约束来进行增删改查。对于tableName/*,一般用于对外暴露一个封装好的接口,即用户不需要了解数据存储的表结构,也可以获取到相应的数据。例如player/getAllTripleDouble,就是获取本赛季所有数据达到场均三双的球员。用户只需要使用contentResolver.query("com.rodneycheung.contentprovidersample.provider/player/getAllTripleDouble",...)就可以获取到所有场均三双球员了。
在了解了内容Uri的定义后,再介绍一个在ContentProvider中常用的Uri匹配的类UriMatcher,它可以帮助我们匹配各个方法中的参数Uri的值。下面直接上做法,非常简单易懂,就不多说了。
在定义好UriMatcher后,我们就不需要手动去解析uri参数并和我们预设的路径进行匹配了。
Android对于这里的MIME类型作了三点格式的规定:
以
vnd开头如果内容Uri是以路径结尾,
vnd后面接android.cursor.dir/,否则接android.cursor.item最后接上
vnd.<authority>.<path>
由于我们只定义了一个针对整个player表的路径,那么我们的MIME字符串就是vnd.android.cursor.dir/vnd.com.rodneycheung.contentprovidersample.provider.player,下面来实现getType方法。
2.4.3 CRUD接口
接下来就是实现CRUD接口,由于我们直接对于player表进行操作,所以非常简单,直接把参数填到dbHelper对应的接口里就可以了。
2.4.4 权限申请
在Android 11中,对于Content Provider的权限又收紧了,需要在AndroidManifest.xml中进行读写权限的申请,具体做法如下。
此外,用户为了能找到这个provider,还需要在自己的AndroidManifest.xml里面加上:
三、总结
结束了,非常的简单😄,完整的代码在这里。
Last updated
Was this helpful?