权限介绍

权限是一种安全机制。Android 权限主要用于限制应用程序内部某些具有限制性特性的功能使用以及应用程序之间的组件访问。说白了就是,你想使用某些功能,必须先声明出来。在 Android6.0 以后,对于不“危险”的权限可以在“声明后”使用,而对于像摄像头这样的“危险”的权限则需要运行时授权。

基本权限

所谓基本权限就是 Android 原生权限,例如网络链接权限,外部文件读写权限,蓝牙权限等等,软件与硬件方面都有。由于基本权限数量较多,此处就不再一一列举了,权限的详细列表可在开发者手册中查询 Manifest.permission | Android Developers

自定义权限

除去 Android 系统提供的权限,开发者也可以自定义权限。使用自定义权限可以实现应用程序之间的组建访问,在 AndroidManifest 文件中来进行权限的自定义。例如:

这样便实现了一个自定义权限,注意 Permission 的各项属性,Name 和
Protection Level(风险等级)是必须的,其它项都可不写。

Name 字段表示权限的名字

Protection Level 字段表示权限的等级:normal dangerous signature signatureOrSystem 对于普通和危险级别的权限,我们称之为低级权限,应用申请即授予。其他两级权限,我们称之为高级权限或系统权限。当应用试图在没有权限的情况下做受限操作,应用将被系统杀掉以警示。系统应用可以使用任何权限。权限的声明者可无条件使用该权限。

以喵喵附近为例


由于推送服务用到了多进程,因此声明了一个名为MIPUSH_SERVICE自定义权限。

权限使用

权限声明

对于需要使用的权限,需要在 AndroidManifest 中的 manifest 标签内使用 uses-permission 声明使用某一个权限 例如:

这样便声明了一个网络连接的权限。关于自定义权限的声明与普通的权限声明完全相同此处不再展开。

Android6.0 运行时权限

上面提到,对于 Android6.0(API23) 引入了运行时权限的概念,即对于“危险”的权限,仅单纯声明也无法使用相应的功能,需要在第一次用户用到此权限的时候运行时确认,同意或拒绝该权限的使用。这些所谓的“危险”权限包括一下几个:

在API23以上的系统使用以上权限,如果不进行运行时动态申请权限则会造成应用闪退,切记。
注意:上图中权限组的概念,同一权限组的权限只要授权了一个,其他也就默认授权了,例如,授权了外部存储读取,那么外部存储的写入也会相应的授权,无须另行请求。

因此对于 API23 以上的运行时权限除了在 Manifest 文件中进行权限声明还需要在代码中作出如下处理:

1.在使用相关功能时判断相关权限是否已经获取:

2.若未授权择要申请权限:

注意:这里使用的是 ActivityCompat 的方法,不是 Activity 本身的方法,这是由于运行时权限是在 API23 时才加入的,因此 Activity 的
requestPermissions 方法最低版本是API23,因此要调用ActivityCompat的方法,以达到统一。
requestPermissions 方法的第二个参数是一个 String 的数组,因此可以一次申请多个权限。

3.申请权限结果处理:

第一次请求权限时,用户拒绝了,shouldShowRequestPermissionRationale() 返回 true,这时候可以显示一些为什么需要这个权限的说明;
第二次请求权限时,用户拒绝了,并选择了“不再提醒”的选项时shouldShowRequestPermissionRationale() 返回 false,此后应用无法再次申请此权限,对于相当重要的权限没有通过,例如下载软件的“外部存储”权限,应用可以强制关闭,避免闪退现象发生。

注意:第二次请求权限时,才会有“不再提醒”的选项,如果用户一直拒绝,并没有选择“不再提醒”的选项,下次请求权限时,会继续有“不再提醒”的选项,并且shouldShowRequestPermissionRationale() 也会一直返回true。

RxPermissions介绍

可以看到运行时权限在提升用户安全的同时,对于开发造成了一定的复杂程度的提升。

那么有没有一个高效的运行时权限的封装库呢??

RxPermissions 是用RxJava实现的一个运行时权限的封装库,使用方便,代码实现清晰,一目了然。Github

基本用法

1.创建一个实例:

2.申请权限:

这样便获取了照相头的权限,进行相关的操作。同样,由于 request 方法接受可变参数,我们还可以一次申请多个权限。例如:

假如我们想获得每个权限的申请情况,而不仅仅是一股脑儿告诉你“你申请的权限没通过”,可以使用 requestEach 方法。

源码分析

知其然知其所以然。
既然 RxPermissions 这么好用我们来简单的了解下其原理,这里我们不深究细节不然会涉及到太多的 RxJava 的技术。我们只需要知道它是个怎么实现的,以及它的原理是什么就好了。(说明一点:为了和项目的RxJava的版本相符,本次采用1.0的源码)

首先我们看一下它的主结构图

RxPermissions.java 是我们这次的关键所在。在看代码之前,我们可以根据上述的调用,来思考一些问题:

1.对于多个权限,可以只返回一个bool值,因此它可能是把几个权限作为一个 Observable 进行判断,封装成一个 Observable 返回的。

2.同时对于多个权限,可以分别返回 Observable 对象,因此有可能是单独对每个权限进行的封装返回的。

带着问题,看源码:
首先是创建实例:

创建并保存了一个 RxPermissionsFragment,然后是 request(String … ) 方法

这里面先 Observable.just(null) 来生成一个空的 Observable 对象。然后通过 compose 操作符来进行转换 将某个参数转换为 Observable 对象并返回。
然后我们看 ensure(String…) 方法:

可以看到 ensure 方法较长,我们一步步分析。首先是定义一个 Transformer,通过这个 Transformer 就可以将任何 Observable对象转换成 Observable 对象了。可以看其返回语句 首先调用了 request(final Observable trigger, final String… permissions)

这个方法通过 flatMap 操作符将 Object 转换成一个 Observable ,而后者就是由通过传入的 String permissions 生成的。在 ensure 函数中,再通过 buffer 操作符,将一个个的 Observable 转换成 Observable> ,最后通过 flatMap 方法对List 进行遍历判断根据其所有的权限授权与否来返回一个Observable 方便订阅。至此也证实了我们的第一个猜想是正确的。

有了上面的基础我们来看后面的代码就方便了许多。首先是 requesEach(String …) 方法

调用了 ensureEach(String …) 方法。

完全一样的思路,不过是对每个 permissions 拆来分别进行Observable封装操作,并且其返回值封装为了 Observable 而不再是Observable ,后面的方法也都是一样的,不再分析。同时也证实了我们的第二个猜想也是正确的。
Rxpermissions 的核心大致就是这些内容。RxPermissionsFragment 封装了一个 Fragment 用于申请权限,没有什么复杂的内容。Permission 封装了权限的相关内容包括是否允许再次请求和是否同意,同样只是一个简单的Bean,不再分析。

综上 Rxpermissions 是个可靠的运行时权限库,对于日常开发帮助很大。

发表评论

电子邮件地址不会被公开。 必填项已用*标注