Android笔记——ContentProvider

ContentProvider主要用于在不同的应用程序之间实现数据共享的功能,允许一个程序访问另一个程序中的数据,同时还能保证被访问数据的安全性。目前,使用ContentProvider是Android实现跨程序共享数据的标准方式。

ContentProvider的用法一般有两种:

  • 一种是使用现有的ContentProvider读取和操作相应程序中的数据;
  • 另一种是创建自己的ContentProvider,给程序的数据提供外部访问接口。

1.0ContentResolver的基本用法

通过Context中的getContentResolver()方法获取ContentResolver类的实例。ContentResolver中提供了一系列的方法用于对数据进行增删改查操作,其中insert()方法用于添加数据,update()方法用于更新数据,delete()方法用于删除数据,query()方法用于查询数据。

ContentResolver中的增删改查方法都是不接收表名参数的,而是使用一个Uri参数代替,这个参数被称为内容URI。
内容URI给ContentProvider中的数据建立了唯一标识符,它主要由两部分组成:authority和path。

  • authority是用于对不同的应用程序做区分的,一般为了避免冲突,会采用应用包名的方式进行命名。
    比如某个应用的包名是com.example.app,那么该应用对应的authority就可以命名为com.example.app.provider。
  • path则是用于对同一应用程序中不同的表做区分的,通常会添加到authority的后面。
    比如某个应用的数据库里存在两张表table1和table2,这时就可以将path分别命名为/table1和/table2,然后把authority和path进行组合,内容URI就变成了com.example.app.provider/table1和com.example.app.provider/table2。

不过,目前还很难辨认出这两个字符串就是两个内容URI,还需要在字符串的头部加上协议声明。因此,内容URI标准的格式如下:


内容URI标准的格式

在得到了内容URI字符串之后,我们还需要将它解析成Uri对象才可以作为参数传入。解析的方法也相当简单,代码如下所示:


解析成Uri对象

1.1查询数据

query()方法定义如下所示:


query()方法定义
query()方法说明

查询完成后返回的是一个Cursor对象,通过移动游标的位置遍历Cursor的所有行,然后取出每一行中相应列的数据,代码如下所示:


查询数据

1.2添加数据

向table1表中添加一条数据,代码如下所示:


添加数据

将待添加的数据组装到ContentValues中,然后调用ContentResolver的insert()方法,将Uri和ContentValues作为参数传入即可。

1.3更新数据

更新这条新添加的数据,把column1的值清空,可以借助ContentResolver的update()方法实现,代码如下所示:


更新数据

使用了selection和selectionArgs参数来对想要更新的数据进行约束,以防止所有的行都会受影响。

1.4删除数据

调用ContentResolver的delete()方法将这条数据删除掉,代码如下所示:


删除数据

2.0使用现有的ContentProvider查询系统联系人

查询系统联系人
  1. ContactsContract.CommonDataKinds.Phone类提供了一个CONTENT_URI常量,而这个常量就是使用Uri.parse()方法解析出来的结果。
  2. 联系人姓名这一列对应的常量是ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME
    联系人手机号这一列对应的常量是ContactsContract.CommonDataKinds.Phone.NUMBER
  3. 最后千万不要忘记将Cursor对象关闭。

修改AndroidManifest.xml中的代码,加入了android.permission.READ_CONTACTS权限,如下所示:


修改AndroidManifest.xml

READ_CONTACTS权限属于危险权限,需要调用运行时权限的处理逻辑,具体使用方法,请看Android笔记——运行时权限

3.0创建自己的ContentProvider

3.1创建ContentProvider的步骤

3.1.1步骤一

要实现跨程序共享数据的功能,可以通过新建一个类去继承ContentProvider的方式来实现。ContentProvider类中有6个抽象方法,在使用子类继承它的时候,需要将这6个方法全部重写。代码示例如下:


代码示例

(1) onCreate():初始化ContentProvider的时候调用。
通常会在这里完成对数据库的创建和升级等操作,返回true表示ContentProvider初始化成功,返回false则表示失败。
(2) query():从ContentProvider中查询数据。
uri参数用于确定查询哪张表
projection参数用于确定查询哪些列
selection和selectionArgs参数用于约束查询哪些行
sortOrder参数用于对结果进行排序,查询的结果存放在Cursor对象中返回
(3) insert():向ContentProvider中添加一条数据。
uri参数用于确定要添加到的表,待添加的数据保存在values参数中。添加完成后,返回一个用于表示这条新记录的URI。
(4) update():更新ContentProvider中已有的数据。
uri参数用于确定更新哪一张表中的数据,新数据保存在values参数中
selection和selectionArgs参数用于约束更新哪些行,受影响的行数将作为返回值返回。
(5) delete():从ContentProvider中删除数据。
uri参数用于确定删除哪一张表中的数据
selection和selectionArgs参数用于约束删除哪些行,被删除的行数将作为返回值返回。
(6) getType():根据传入的内容URI返回相应的MIME类型。

3.1.2步骤二

一个标准的内容URI写法是:


com.example.app应用的table1表中的数据

com.example.app应用的table1表中id为1的数据。

内容URI的格式主要就只有以上两种:

  1. 以路径结尾表示期望访问该表中所有的数据,
  2. 以id结尾表示期望访问该表中拥有相应id的数据。
    可以使用通配符分别匹配这两种格式的内容URI,规则如下:
    ● *表示匹配任意长度的任意字符。
    ● #表示匹配任意长度的数字。


    匹配任意表的内容URI格式

    匹配table1表中任意一行数据

借助UriMatcher这个类可以实现匹配内容URI的功能。

  • UriMatcher中提供了一个addURI()方法,可以分别把authority、path和一个自定义代码传进去。
  • 当调用UriMatcher的match()方法时,就可以将一个Uri对象传入,返回值是某个能够匹配这个Uri对象所对应的自定义代码,利用这个代码,我们就可以判断出调用方期望访问的是哪张表中的数据了。

修改MyProvider中的代码,如下所示:


修改MyProvider代码

table1Dir表示访问table1表中的所有数据
table1Item表示访问table1表中的单条数据
table2Dir表示访问table2表中的所有数据
table2Item表示访问table2表中的单条数据

在MyProvider类实例化的时候创建UriMatcher的实例,调用addURI()方法,将期望匹配的内容URI格式传递进去。当query()方法被调用的时候,就会通过UriMatcher的match()方法对传入的Uri对象进行匹配,如果发现UriMatcher中某个内容URI格式成功匹配了该Uri对象,则会返回相应的自定义代码,然后就可以判断出调用方期望访问的到底是什么数据了。

insert()、update()、delete()这几个方法的实现是差不多的,它们都会携带uri这个参数,然后同样利用UriMatcher的match()方法判断出调用方期望访问的是哪张表,再对该表中的数据进行相应的操作就可以了。

3.1.3步骤三

getType()方法是所有的ContentProvider都必须提供的一个方法,用于获取Uri对象所对应的MIME类型。
一个内容URI所对应的MIME字符串主要由3部分组成,Android对这3个部分做了如下格式规定。

● 必须以vnd开头
● 如果内容URI以路径结尾,则后接android.cursor.dir/
如果内容URI以id结尾,则后接android.cursor.item/
● 最后接上vnd.<authority>.<path>

对于content://com.example.app.provider/table1这个内容URI,它所对应的MIME类型就可以写成:

MIME类型

对于content://com.example.app.provider/table1/1这个内容URI,它所对应的MIME类型就可以写成:
MIME类型

完善MyProvider中的getType()方法逻辑,代码如下所示:


getType()方法逻辑

一个完整的ContentProvider就创建完成了,现在任何一个应用程序都可以使用ContentResolver访问我们程序中的数据。

那么,如何才能保证隐私数据不会泄漏出去呢?
因为所有的增删改查操作都一定要匹配到相应的内容URI格式才能进行,而我们当然不可能向UriMatcher中添加隐私数据的URI,所以这部分数据根本无法被外部程序访问,安全问题也就不存在了。

3.2实现自定义跨程序数据共享

3.2.1数据提供方

代码如下:


ContentProvider定义
onCreate()
query()
insert()
update()
delete()
getTyoe()

ContentProvider一定要在AndroidManifest.xml文件中注册才可以使用。


AndroidManifest.xml文件

<application>标签内出现了一个新的标签<provider>,使用它来对DatabaseProvider进行注册。

  • android:name属性指定了DatabaseProvider的类名
  • android:authorities属性指定了DatabaseProvider的authority
  • android:enabled属性表示是否启用这个ContentProvider
  • android:exported属性表示是否允许外部程序访问我们的ContentProviderl

3.2.2数据访问方

添加数据
查询数据
更新数据
删除数据
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,383评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,522评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,852评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,621评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,741评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,929评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,076评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,803评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,265评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,582评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,716评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,395评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,039评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,027评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,488评论 2 361
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,612评论 2 350

推荐阅读更多精彩内容