ContentProvider数据库共享之——读写权限与数据监听

一、数据库读写权限

1、概述

在AndroidManifest.xml中provider标签中有三个额外的参数permission、readPermission、writePermission;
先看下面这段代码:

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <provider  
  2.     android:name=".PeopleContentProvider"  
  3.     android:authorities="com.harvic.provider.PeopleContentProvider"  
  4.     android:exported="true"  
  5.     android:permission="com.harvic.contentProviderBlog"  
  6.     android:readPermission="com.harvic.contentProviderBlog.read"  
  7.     android:writePermission="com.harvic.cotentProviderBlog.write"/>  
在这段代码中有几个参数要特别注意一下:
  • exported:这个属性用于指示该服务是否能被其他程序应用组件调用或跟他交互; 取值为(true | false),如果设置成true,则能够被调用或交互,否则不能;设置为false时,只有同一个应用程序的组件或带有相同用户ID的应用程序才能启动或绑定该服务。具体参见:《Permission Denial: opening provider 隐藏的android:exported属性的含义》
  • readPermission:使用Content Provider的查询功能所必需的权限,即使用ContentProvider里的query()函数的权限;
  • writePermission:使用ContentProvider的修改功能所必须的权限,即使用ContentProvider的insert()、update()、delete()函数的权限;
  • permission:客户端读、写 Content Provider 中的数据所必需的权限名称。 本属性为一次性设置读和写权限提供了快捷途径。 不过,readPermission和writePermission属性优先于本设置。 如果同时设置了readPermission属性,则其将控制对 Content Provider 的读取。 如果设置了writePermission属性,则其也将控制对 Content Provider 数据的修改。也就是说如果只设置permission权限,那么拥有这个权限的应用就可以实现对这里的ContentProvider进行读写;如果同时设置了permission和readPermission那么具有readPermission权限的应用才可以读,拥有permission权限的才能写!也就是说只拥有permission权限是不能读的,因为readPermission的优先级要高于permission;如果同时设置了readPermission、writePermission、permission那么permission就无效了。

2、实例

(1)有关自定义权限

由于在privoder标签中要用到自定义权限,比如:

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <provider  
  2.     android:name=".PeopleContentProvider"  
  3.     android:authorities="com.harvic.provider.PeopleContentProvider"  
  4.     android:exported="true"  
  5.     android:readPermission="com.harvic.contentProviderBlog.read"/>  
比如,在这里我们定义一个readPermission字符串,单纯写一个字符串是毫无意义的,因为根本系统的任何权限都是要申请的,没有申请过的一串String代表的字符串系统根本无法识别,也就是说谁进来都会被挡在外面!
所以在application标签的同级目录,写上申请permission的代码:
[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <permission  
  2.     android:name="com.harvic.contentProviderBlog.read"  
  3.     android:label="provider pomission"  
  4.     android:protectionLevel="normal" />  
这样,我们的permission才会在系统中注册,在第三方应用中使用<uses-permission来声明使用权限时,才有意义;如果不申请,那么系统中是不存在这个权限的,那么虽然你使用了<uses-permission来申请使用权限,但系统中根本找不到这个权限,所以到provider匹配时,就会出现permission-deny的错误;
[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <uses-permission android:name="com.harvic.contentProviderBlog.read"/>  
有关自定义权限的内容,请参考 《声明、使用与自定义权限》

(2)、带有权限的ContentProvder实例

首先在ContentProviderBlog中首先向系统申请两个权限:
分别对应读数据库权限和写数据库权限

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <permission  
  2.     android:name="com.harvic.contentProviderBlog.read"  
  3.     android:label="provider read pomission"  
  4.     android:protectionLevel="normal" />  
  5. <permission  
  6.     android:name="com.harvic.contentProviderBlog.write"  
  7.     android:label="provider write pomission"  
  8.     android:protectionLevel="normal" />  
情况一:使用读写权限
然后在provider中,我们这里同时使用读、写权限:

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <provider  
  2.     android:name=".PeopleContentProvider"  
  3.     android:authorities="com.harvic.provider.PeopleContentProvider"  
  4.     android:exported="true"  
  5.     android:readPermission="com.harvic.contentProviderBlog.read"  
  6.     android:writePermission="com.harvic.cotentProviderBlog.write" />  
在这种情况下,使用第三方应用同时申请使用这两个权限才可以对数据库读写,即:
[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <uses-permission android:name="com.harvic.contentProviderBlog.read"/>  
  2. <uses-permission android:name="com.harvic.contentProviderBlog.write"/>  
情况二:仅添加读权限
如果我们在provider中,仅添加读共享数据库的权限,那第三方应该怎么申请权限才能读写数据库呢?

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <provider  
  2.     android:name=".PeopleContentProvider"  
  3.     android:authorities="com.harvic.provider.PeopleContentProvider"  
  4.     android:exported="true"  
  5.     android:readPermission="com.harvic.contentProviderBlog.read"/>  
从上面可以看到,我们只添加了readPermission,那第三方应用在不申请任何权限的情况下是可以写数据库,但当读数据库时就需要权限了; 即如果要读数据库需要添加如下使用权限声明:
[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <uses-permission android:name="com.harvic.contentProviderBlog.read"/>  
在上一篇的基础上,代码不需要动,只需要在AndroidManifest.xml中写上权限定义与声明,即可;
(3)、效果:

在实例代码中,在provider中添加了读写权限,但在第三方APP:UseProvider中,仅添加了读权限,所以在利用URI来读共享数据库的时候结果是正常的,但在写数据库时,就会抛出permission-denied错误,如果不用try...catch...捕捉,就会造成crash;结果截图如下:

  • query()操作


  • insert()、delete() 或 update()操作时,就会抛出异常



源码在文章底部给出。

二、数据监听

1、监听步骤

要监听指定URI上的数据库变化,在监听方需要两步:

(1)、生成一个类派生自ContentObserver

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public class DataBaseObserver extends ContentObserver {  
  2.     public DataBaseObserver(Handler handler) {  
  3.         super(handler);  
  4.     }  
  5.   
  6.     @Override  
  7.     public void onChange(boolean selfChange) {  
  8.         super.onChange(selfChange);  
  9.         Log.d("harvic","received first database changed");  
  10.     }  
  11. }  
注意这里会自动生成一个onChange()方法,当有改变到来时就会跑到onChange()里,所以我们在这里面打一个LOG,注意LOG内容:“received first database changed”,很明显这里我只用DataBaseObserver来监听"content://com.harvic.provider.PeopleContentProvider/frist "所对应的数据库进行监听,至于如何指定URI往下看
(2)、注册和反注册监听

  • 注册

在使用URI来操作第三方数据之前,先进行ContentObserver注册,这里我们在MainActivity的OnCreate()函数里注册:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. private DataBaseObserver observer;  
  2.   
  3. @Override  
  4. protected void onCreate(Bundle savedInstanceState) {  
  5.     super.onCreate(savedInstanceState);  
  6.     setContentView(R.layout.activity_main);  
  7.     observer = new DataBaseObserver(new Handler());  
  8.     //注册指定URI 变动监听  
  9.     getContentResolver().registerContentObserver(CONTENT_URI_FIRST,true,observer);  
  10. }  
这里先完整说完流程,等下再讲registerContentObserver()的参数
  • 在OnDestory()时,反注册:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2. protected void onDestroy() {  
  3.     super.onDestroy();  
  4.     //取消监听  
  5.     getContentResolver().unregisterContentObserver(observer);  
  6. }  
(3)、在ContentProvider中数据库变动通知
在ContentProvider中指定URI上有数据库数据变动时,及时利用下面的函数来通知

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. getContext().getContentResolver().notifyChange(uri, null);  
这个函数就是在第二篇中,我们提到的通知函数;
好了,到这数据库变动通知和监听的过程就讲完了,下面看看registerContentObserver()注册监听函数的用法:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public final void registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer)  
功能:为指定的Uri注册一个ContentObserver派生类实例,当**给定**的Uri发生改变时,回调该实例对象去处理。
参数:

  • uri :     需要观察的Uri
  • notifyForDescendents:  为false 表示精确匹配,即只匹配该Uri; 为true 表示可以同时匹配其派生的Uri

举例如下:假设UriMatcher 里注册的Uri共有一下类型:
1、content://com.qin.cb/student (学生)
2、content://com.qin.cb/student/# 
3、content://com.qin.cb/student/schoolchild(小学生,派生的Uri) 
假设我们当前需要观察的Uri为content://com.qin.cb/student,如果发生数据变化的 Uri 为 content://com.qin.cb/student/schoolchild;那么当notifyForDescendents为 false,那么该ContentObserver会监听不到, 但是当notifyForDescendents 为ture,能捕捉该Uri的数据库变化。

  • observer: ContentObserver的派生类实例

2、结果

在数据库改变时,收到消息如图所示:


好了,这到就结束了,有关ContentProvider的用法也都讲完了;

源码在文章最底部

三、补充:SetProjectionMap——投影映射

在上面的实例中,我们对外提供的数据库列名和内部使用的是一样的,但这样很容易被对方知道我们的数据库结构,那要想自己本地使用一套列名,给外部提供另一套对应的列名,这样别人不就猜不出我们的列名了么,针对这个问题,在SQLiteQueryBuilder类中,为我们提供了内外部列名映射函数,以允许我们在外部和内部列名不同时的提供映射功能。

主要使用的函数是setProjectionMap(HashMap map)

使用方法如下:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();  
  2. Map projectionMap = new HashMap<String,String>();  
  3. projectionMap.put("out_column_name_1","inside_column_name_1");  
  4. projectionMap.put("out_column_name_2","inside_column_name_2");  
  5. projectionMap.put("out_column_name_3","inside_column_name_3");  
  6. queryBuilder.setTables(DatabaseHelper.TABLE_FIRST_NAME);  
  7. queryBuilder.setProjectionMap(projectionMap);  

由于是事后补充的知识,在这里就再给大家提供例子参考了,大家可以自己尝试下。


感谢下面这些精彩的文章给了我参考的资料和灵感:

1、《android ContentProvider使用详解》(很棒)

2、《android之ContentProvider和Uri详解》

3、《Android突破笔记五:ContentProvider和Uri详解》

4、《自定义ContentProvider的一些细节探究》

5、《android ContentResolver详解》

6、《对android中MIME类型以及contentprovidergetType()方法的理解》

7、《ContentProvider中getType(Uri uri)用法及理解》

8、《是否需要覆盖ContentProvider的getType方法?》

9、《对ContentProvider中getType(Uri uri)和android.intent.category.DEFAULT的理解》 (很棒)

10、《ContentProvider中gettype() 和MIME类型的理解》

11、《监测database的改变--notifyChange》

12、《Android 监听ContentProvider中数据的变化》

13、《Android监听数据库的值改变与否》

14、《Permission Denial: opening provider 隐藏的android:exported属性的含义》

15、《Content Provider的权限 》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值