Android 动态权限机制详解

一.权限系统

(一)6.0之前

Android为一些操作提供了必要的权限,比如我们的app想访问网络,就需要有网络权限android.permission.INTERNET、想使用相机功能,需要相机权限android.permission.CAMERA等,我们可以在Manifest文件里声明我们的想要的权限:

<uses-permission android:name="android.permission.INTERNET" />

在6.0系统诞生之前,android系统对权限的默认操作是,在安装app时询问用户是否授予这些权限,如果不授予则不能安装,如果授予,则安装app,且app自动获取了我们声明的所有权限,之后也不能改变授予状态,这就是静态权限

(二)6.0之后

  1. 从6.0系统开始,android将权限分为了普通权限和危险权限两类,对于普通权限,和原有权限授予方式一样,也是属于静态权限;

    对于危险权限,则在app安装时不提示用户授予,而是在使用到具体的权限时,才会提示用户是否授予,并且用户可以手动开启/关闭某个危险权限,这就是动态权限,这样做可以简化安装流程,并将一些危险权限的控制权交由用户,增加用户友好度和透明度

  2. 权限也是分组的,如果一个权限组里的一个权限被授予,那么这个组内其他权限也会被授予,但是权限组以后可能会更改,所以为了向前兼容,app不应该以假设权限组的方式管理权限

  3. 危险权限分组列表

    https://developer.android.com/guide/topics/security/permissions.html?#normal-dangerous

(三)8.0

8.0系统(targetSdkVersion>=26)开始,当用户申请某个权限时,不会立刻把同组的权限一并赋予,但是当下次再请求同组其他权限时,会直接授予该权限,而不会提示用户;

不过6.0时已经说明,开发者不应该依赖于权限组做开发,所以设计良好的话,此处不应该会造成问题

二.兼容适配

(一)TargetSdkVersion

动态权限推出后,如果我们的应用没有做权限适配,是不是就会因为没有权限导致崩溃呢?当然不是,google肯定有兼容的办法,那就是我们应用配置的targetSdkVersion,这个属性就是指明了app所运行的API级别:当Android推出新的系统,有了新的行为特性,但是我们还没有适配新的行为,就可以让app先按老版本的行为去执行,这个老的版本就是我们指定的targetSdkVersion

假设我们的应用现在没有适配6.0(API 23)权限,那么我们可以把targetSdkVersion设置为23以下,这样,不管代码运行在哪个版本系统上,都会以23以下的那个版本的行为去执行,就不会有问题;反过来说就是,当我们把targetSdkVersion升级到23以上时,就必须要对动态权限做适配了,否则按照新的系统行为去执行代码,就可能会因为没有权限而有问题,具体情况可以分为:

  1. Android系统<6.0,targetSdkVersion<23:静态权限

  2. Android系统<6.0,targetSdkVersion>=23:静态权限

  3. Android系统>=6.0,targetSdkVersion<23:按照静态权限处理(安装时授权),但是用户仍然可以手动关闭权限,系统有默认处理方式,但是可能会有些异常情况

  4. Android系统>=6.0,targetSdkVersion>=23:按照动态权限处理,需要做适配,否则在没有权限的情况执行操作会崩溃

(二)适配方法

1.检查权限

Context.checkSelfPermisiion(String permission)

Context提供了该方法用于检测当前应用是否被授予了某项权限

也可以用ContextCompat.checkSelfPermission(Context context,String permission)方法,方法内部用的就是Context的该方法

对于6.0以上的系统,该方法自然返回当前权限的状态,对于6.0以下的系统,则会统一返回授予状态(因为6.0之前是静态权限)

2.请求权限

6.0系统开始,Activity提供了方法

Activity.requestPermissions(String[] permissions,int requestCode)

该方法可以请求多个权限:使用startActivityForResult,弹出一个Dialog类型的Activity让用户选择授予权限还是拒绝权限

因为6.0之前的Activity没有该方法,为了统一兼容,可以使用ActivityCompat.requestPermissions(Activity activity,String[] permissions,int requestCode),该方法在6.0以上的系统直接调用Activity的requestPermissions方法,在6.0以下的系统,会回调一个onRequestPermissionsResult(int requestCode,String[] permissions,int[] grantResult)接口方法,权限的状态就是当前权限的状态(因为6.0之前为静态权限)

3.请求权限的结果

由于是通过startActivityForResult开启新的Activity来获取权限,那么回调自然就是回到请求的Activity的dispatchActivityResult方法,由于request时会记录标志位,所以Activity在dispatchActivityResult里进行判断,并不会执行onActivityResult,而是走到onRequestPermissionsResult(int requestCode,String[] permissions,int[] grantResult)方法进行回调(与上述适配接口方法同名,实现兼容)

该方法参数permissions就是请求的权限,grantResult就是权限的状态

4.适当的提示

对于权限请求,用户当然可以拒绝,甚至可以点击"不再询问"按钮(这种情况下每次requestPermission都会直接返回拒绝状态而不会提示用户),这些情况下,为了用户体验,也为了我们app能正常使用,我们应该在适当的时候提示用户为什么要这些权限,以及如何打开这些权限吧?于是乎,android提供了Activity.shouldShowRequestPermissionRationale(String permission)这个方法

该方法在用户第一次请求权限前都是返回false,在用户每次拒绝后都返回true,在用户勾选了“不再询问”的情况下拒绝后,返回false,加入我们在用户拒绝后想提示用户,是不是就可以这样呢:

  1. 如果shouldShowRequestPermissionRationale方法返回true,说明用户只是拒绝了,没有勾选"不再询问",就可以提示用户这个权限是干啥啥啥的,不授予的话就不行,于是乎再次请求时用户就可能授权了

  2. 如果shouldShowRequestPermissionRationale方法返回false,说明用户拒绝了,并且勾选了"不再询问",这时该权限不会再弹出对话框提示用户了,就需要我们手动谈Dialog,提示用户:这个功能需要Xxx权限,请到设置->应用→权限里手动开启权限

当然,我们也可以使用ActivityCompat.shouldShowRequestPermissionRationale(Activity activity,String permission)来进行兼容,不过讲真,这个方法其实是通过Context的getPackageManager来实现的,不应该和Activity绑定。。。

5.Fragment的权限请求

权限在Activity里有了校验的方法,自然少不了Fragment的了,所以在Fragment里也有相应的requestPermissions和shouldShowRequestPermissionRationale方法,并提供了onRequestPermissionsResult回调,本质其实就是调用了FragmentActivity的相应方法,并在request时记录Fragment信息,回调时回调给Fragment,没什么可说的

6.流程图总结

Fragment和Activity权限接口类图
在这里插入图片描述
Activity请求权限流程图
在这里插入图片描述
Fragment请求权限流程图
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值