1、前言
自从 Android6.0 发布,增加了许多新的特性和功能,除了强化和完善了 MD 设计元素,Android的安全也得到了谷歌的重视。于是,Android6.0 中出现了运行时权限的概念。许多程序员前赴后继,推出了大量的优秀的第三方库,来简化运行时权限的使用。但是,我觉得我们有必要从根本上学会使用运行时权限的申请,这能增加我们对 Permission 的理解。
本教程代码:https://github.com/sendtion/SDCPermission
2、运行时权限
当我们的 targetSdkVersion>=23 时,我们一定要注意权限的申请,否则 App 会因为没有获取权限而发生崩溃。当然,在 API 23 以下系统版本也可以判断一下权限是否得到,确保万无一失。
我们不能连续一次性申请多个权限,这会使用户感觉到不安全,感觉 App 有窃取隐私信息的可能,可能因此丧失一部分用户。也不能在 App 内到处申请权限,这会是用户感到烦躁和麻烦,这也会丧失一部分用户。
所以,我们申请权限的时候一定要注意一些原则:
a>尽量精简权限,剔除不必要的权限
大量的权限,只会让用户丧失安全感或者感到烦躁,得不偿失。
b>用户拒绝后再次请求一定要详细说明缘由
给用户详细说明,我们需要这个权限来干什么,用户才更容易接受。
c>在合理的位置申请权限
不要一次性申请所有的权限,这中感觉很不好,会让用户感觉自己被强 X 了。
3、系统权限分类
Google 将权限分为两类,一类是普通权限(Normal Permissions),这类权限一般不涉及用户隐私,也不需要用户进行授权,如网络访问,手机震动等,这些权限如下所示:
<div>::CODECOLORER_BLOCK_1::</div>
另外一类是危险权限(Dangerous Permission),涉及用户隐私,需要用户授权,如对 sd 卡读取、访问用户手机通讯录等。如下所示:
<div>::CODECOLORER_BLOCK_2::</div>
查看 Dangerous Permission 可以发现权限是分组的,这个和 Android 6.0 的授权机制有关。如果你申请某个危险的权限,假设你的 app 早已被用户授权了同一组的某个危险权限,那么系统会立即授权,而不需要用户去点击授权。比如你的 app 对 READ_CONTACTS 已经授权了,当你的 app 申请 WRITE_CONTACTS 时,系统会直接授权通过。此外,申请时弹出的 dialog 上面的文本说明也是对整个权限组的说明,而不是单个权限(ps:这个 dialog 是不能进行定制的)。
注:不要对权限组过多的依赖,尽可能对每个危险权限都进行正常流程的申请,因为在后期的版本中这个权限组可能会产生变化。
4、权限申请方式
以请求读取联系人权限为例:
a> 在 AndroidManifest.xml 文件中声明所需要的权限
b> 检查是否授予了所需要的权限
<div>::CODECOLORER_BLOCK_3::</div>
ContextCompat.checkSelfPermission()主要用于检测某个权限是否已经被授予,方法返回值为 PackageManager.PERMISSION_DENIED 或者 PackageManager.PERMISSION_GRANTED。当返回 DENIED 就需要进行申请授权了。
c> 如果没有授予权限,则请求授权
<div>::CODECOLORER_BLOCK_4::</div>
该方法是异步的,第一个参数是 Context;第二个参数是需要申请的权限的字符串数组;第三个参数为 requestCode,主要用于回调的时候检测。可以从方法名 requestPermissions 以及第二个参数看出,是支持一次性申请多个权限的,系统会通过对话框逐一询问用户是否授权。
d> 处理权限申请的结果
<div>::CODECOLORER_BLOCK_5::</div>
e> 如果用户拒绝了,下次申请给予解释
<div>::CODECOLORER_BLOCK_6::</div>
这个 API 主要用于给用户一个申请权限的解释,该方法只有在用户在上一次已经拒绝过你的这个权限申请。也就是说,用户已经拒绝一次了,你又弹个授权框,你需要给用户一个解释,为什么要授权,则使用该方法。
注:
* 如果应用之前请求过此权限但用户拒绝了请求,此方法将返回 true。
* 如果用户在过去拒绝了权限请求,并在权限请求系统对话框中选择了 Don’t ask again 选项,此方法将返回 false。
* 如果设备规范禁止应用具有该权限,此方法也会返回 false。
5、优化
到这里算是处理完了,这么看起来并没有多麻烦,但是坑还是存在的。Android 碎片化严重,大家都知道。国产的各种定制系统深度修改,好多底层 API 都有所改变,给我们 Android 开发者带来了极大的麻烦。诸如 MIUI、Flyme、EMUI、FuntouchOS、ColorOS、SmartisanOS、CoolUI、H2OS 等等等,各种各样,五花八门。
本教程的例子用的是小米 5 MIUI8 7.0 开发版,就遇到了很坑的事情。
小米默认是允许读写存储卡权限的,其他的权限都是询问状态。
经测试,
默认询问,除了日历、录音、相机、存储卡会弹窗,你可以选择拒绝或者允许;其他权限不弹窗,checkSelfPermission 直接返回 0,也就是直接允许了,此时权限状态仍然为询问状态;
手动拒绝后不会再弹窗,相当于选中了不再询问,权限状态由询问变为拒绝,需要手动给予权限;
手动允许后,权限状态由询问变为允许;
MIUI 上:shouldShowRequestPermissionRationale()返回一直是 false,无法进行判断解释。
GitHub 地址:https://github.com/sendtion/SDCPermission
完整的代码如下:
<div>::CODECOLORER_BLOCK_7::</div>
6、第三方库推荐
第三方库推荐,列举的有点多,至于你喜欢哪个,需要自己去发现~
前三个都不错,个人用的第四个!
yanzhenjie/AndPermission: Android permission.
https://github.com/yanzhenjie/AndPermission (严振杰)
hongyangAndroid/MPermissions: a easy API to use runtime permission for Android M
https://github.com/hongyangAndroid/MPermissions (鸿洋)
lovedise/PermissionGen: Android API easy to use permission for Android M
https://github.com/lovedise/PermissionGen
tbruyelle/RxPermissions: Android runtime permissions powered by RxJava 支持 RxJava 的动态权限
https://github.com/tbruyelle/RxPermissions
forJrking/HeiPermission: Hei 一句代码搞定 Android M 动态权限检测。信不信由你,反正我用了。
https://github.com/forJrking/HeiPermission
rebus007/PermissionUtils: Check marshmallow permission easily
https://github.com/rebus007/PermissionUtils
mylhyl/AndroidAcp: 一句话搞定,简化 Android 6.0 系统复杂的权限操作
https://github.com/mylhyl/AndroidAcp
lypeer/FcPermissions: Fuck the permissions in Android M
https://github.com/lypeer/FcPermissions
Airsaid/MPermissionUtils: Android6.0 运行时权限 超轻量级工具类
https://github.com/Airsaid/MPermissionUtils
AndSync/XPermissionUtils: 可能是最精简的 Android6.0 运行时权限处理方式
https://github.com/AndSync/XPermissionUtils
如有疑问,欢迎指正!
个人简介:
GitHub:https://github.com/sendtion
博客:http://sendtion.cn/
简书:http://www.jianshu.com/users/630d98ed1424