一、数据库读写权限
1、概述
在AndroidManifest.xml中provider标签中有三个额外的参数permission、readPermission、writePermission;
先看下面这段代码:
在这段代码中有几个参数要特别注意一下:
- 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标签中要用到自定义权限,比如:
比如,在这里我们定义一个readPermission字符串,单纯写一个字符串是毫无意义的,因为根本系统的任何权限都是要申请的,没有申请过的一串String代表的字符串系统根本无法识别,也就是说谁进来都会被挡在外面!
所以在application标签的同级目录,写上申请permission的代码:
这样,我们的permission才会在系统中注册,在第三方应用中使用<uses-permission来声明使用权限时,才有意义;如果不申请,那么系统中是不存在这个权限的,那么虽然你使用了<uses-permission来申请使用权限,但系统中根本找不到这个权限,所以到provider匹配时,就会出现permission-deny的错误;
有关自定义权限的内容,请参考 《声明、使用与自定义权限》
(2)、带有权限的ContentProvder实例
首先在ContentProviderBlog中首先向系统申请两个权限:
分别对应读数据库权限和写数据库权限
情况一:使用读写权限
然后在provider中,我们这里同时使用读、写权限:
在这种情况下,使用第三方应用同时申请使用这两个权限才可以对数据库读写,即:
情况二:仅添加读权限
如果我们在provider中,仅添加读共享数据库的权限,那第三方应该怎么申请权限才能读写数据库呢?
从上面可以看到,我们只添加了readPermission,那第三方应用在不申请任何权限的情况下是可以写数据库,但当读数据库时就需要权限了; 即如果要读数据库需要添加如下使用权限声明:
在上一篇的基础上,代码不需要动,只需要在AndroidManifest.xml中写上权限定义与声明,即可;
(3)、效果:
在实例代码中,在provider中添加了读写权限,但在第三方APP:UseProvider中,仅添加了读权限,所以在利用URI来读共享数据库的时候结果是正常的,但在写数据库时,就会抛出permission-denied错误,如果不用try...catch...捕捉,就会造成crash;结果截图如下:
- query()操作
- insert()、delete() 或 update()操作时,就会抛出异常
源码在文章底部给出。
二、数据监听
1、监听步骤
要监听指定URI上的数据库变化,在监听方需要两步:
(1)、生成一个类派生自ContentObserver
注意这里会自动生成一个onChange()方法,当有改变到来时就会跑到onChange()里,所以我们在这里面打一个LOG,注意LOG内容:“received first database changed”,很明显这里我只用DataBaseObserver来监听"content://com.harvic.provider.PeopleContentProvider/frist "所对应的数据库进行监听,至于如何指定URI往下看
(2)、注册和反注册监听
- 注册
在使用URI来操作第三方数据之前,先进行ContentObserver注册,这里我们在MainActivity的OnCreate()函数里注册:
这里先完整说完流程,等下再讲registerContentObserver()的参数
- 在OnDestory()时,反注册:
(3)、在ContentProvider中数据库变动通知
在ContentProvider中指定URI上有数据库数据变动时,及时利用下面的函数来通知
这个函数就是在第二篇中,我们提到的通知函数;
好了,到这数据库变动通知和监听的过程就讲完了,下面看看registerContentObserver()注册监听函数的用法:
功能:为指定的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)
使用方法如下:
由于是事后补充的知识,在这里就再给大家提供例子参考了,大家可以自己尝试下。
感谢下面这些精彩的文章给了我参考的资料和灵感:
1、《android ContentProvider使用详解》(很棒)
2、《android之ContentProvider和Uri详解》
3、《Android突破笔记五:ContentProvider和Uri详解》
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中数据的变化》
14、《Permission Denial: opening provider 隐藏的android:exported属性的含义》