可能你会有些疑惑,为什么要将我们程序中的数据共享给其他程序呢?当然,这是视情况而定的,比如账号密码之类的隐私数据显然是不能共享给其他程序的,不过一些可以让其他程序进行二次开发的基础性数据,我们还是可以选择共享。例如联系人程序、短信程序、多媒体库等,它们的数据库中保存了很多基础数据,如果不允许其他应用进行访问,则方便性就会大打折扣。
运行时权限
Android的权限机制在一开始就存在,但是在6.0以前保护隐私方面比较有限,因为像微信这种大家都离不开的软件,容易“店大欺客”,不同意它所有的权限只能不安装,这并不合理。
权限机制详解
开发者在AndroidManifest.xml中声明权限,一种情况是,用户如果在低于6.0的系统上安装该程序,会在安装时列出该应用所需要的权限,从而决定是否要安装这个程序,并且在用户安装成功之后,还能在设置中查看程序所申请的权限,但是对于那些离不开的程序(比如微信)来说,要么全部同意它申请的权限,要么不安装,这不太合理;如果在6.0及以上的系统中安装,则用户不必在安装时一次性授权所有申请的权限,而是在软件使用的过程中再对危险权限进行授权,就算拒绝了这个权限,仍然可以使用应用的其他功能,而不是以前那样直接无法安装。
Android 6.0 及以上将所有权限分为两类,普通权限和危险权限,普通权限是指不会直接威胁用户的安全和隐私的权限,这部分权限系统自动帮我们授权,避免用户不停地手动授权;危险权限则表示会触及用户隐私或者设备安全性的权限,如获取联系人、定位设备位置等,必须由程序员动态申请,由用户手动点击授权才可以,否则无法使用相应功能。目前为止,Android中的危险权限有9组共24个权限,如下列表所示(图片来自官网):
这张表格无需记住,在使用的时候作为参照,如果权限在这张表中,则进行运行时处理就好。另外注意一下,表格中每个危险权限都属于一个权限组,我们在进行运行时权限处理时使用的是权限名,但是用户一旦同意授权了,那么该权限对应的权限组中所有的其他权限也会同时被授权。
在程序运行时申请权限
以拨打电话的权限为例来说明权限的申请,点击一个按钮,就拨打指定的号码,在6.0以前可能是这样实现的:
- 在AndroidManifest.xml中申请权限:
1
<uses-permission android:name="android.permission.CALL_PHONE"/>
- 在代码中实现:
1 | btnCall.setOnclickListener(new View.OnclickListener(){ |
在6.0以下系统上能正常拨打电话,但是在6.0或者以上系统运行,会报错Permission Denial,可以看出是由于权限被禁止导致的,因此我们应该尝试使用以下方式来申请权限:
1 | btnCall.setOnclickListener(new View.OnclickListener(){ |
上述第一步先判断用户是不是已经给我们授权了,使用的是ContextCompat.checkSelfPermission,如果已经授权,直接拨打电话,否则调用ActivityCompat.requestPermission方法向用户申请授权,这时候用户可以选择同意或者拒绝我们的申请,不论哪种结果,都会通过回调onRequestPermissionResult告知,在回调中根据不同的结果做不同的处理。记住,在动态声明权限后,AndroidManifest中还得添加
访问其他程序中的数据
内容提供器的用法一般有两种,一是使用现有的内容提供器来读取和操作响应程序中的数据,另一种是创建自己的内容提供器给我们的数据提供外部访问接口。
ContentResolver的基本使用
如果想要访问内容提供器共享的数据,就一定要借助ContentResolver类,可以通过Context中的getContentResolver方法获取到该类的实例。可以对内容进行CRUD操作,不同于SQLiteDatabase,ContentResolver增删改查不接收表名参数,而是使用Uri参数代替,该Uri主要由两部分组成:authority和path,前者用于对不同的应用程序做区分,一般采用程序包名形式,如某个程序的包名是com.example.app,那么对应的authority就可以命名为com.example.app.provider;path则是对同一应用程序中不同表做区分的,通常会添加到authority后面,所以内容Uri的形式一般如下所示(带协议声明):
content://com.example.app.provicer/table1
content://com.example.app.provicer/table2
正式查询的时候,将Uri作为参数传入,代码如下:
1 | Uri uri = Uri.parse("content://com.example.app.provicer/table1"); |
其中,query方法中各个参数对应的含义如下所示:
接下来便可以进行相应的增删改查操作,代码如下:
1 | //查 |
1 | //增 |
1 | //改,把column1的值清空 |
1 | //删除 |
其实整体就相当于sql语句,因此并不太难。
创建自己的内容提供器
因为基本上没有这样的需求,暂时略后续补上