Android---动态权限适配问题

在 Android6.0,即 API 23 之前,App 需要的权限都会在安装阶段向用户展示,而在 App 运行期间不需要动态判断权限是否已申请。从 6.0 之后的版本开始,Android 系统做了一次大的改动。对于部分权限,App 需要在代码中动态申请相应的权限。

权限分类

Android 权限分两种:普通权限和危险权限。在两种权限都需要在 AndroidManifest.xml 清单文件中声明。

普通权限(Normal Permission):在程序运行时期自动获取,只需要在清单文件中声明。常用的是 INTERNET 网络权限。

危险权限(Dangerous Permission):App 中可能存在一些操作会查看与用户隐私相关的信息,比如查看用户的通讯录或者图库等。对于这一类操作,Android 系统要求 App 主动向用户展示操作所需要的权限。只有用户授权之后,才可以进行下一步操作。

权限动态申请流程

一次完整的权限申请流程,如下图所示

对上图申请流程做一个简单说明

首先,判断 API 版本是否小于 23。如果版本低于23,则不需要动态申请权限,否则调用 checkSelfPermission() 方法检查权限是否已申请。如果,checkSelfPermission 返回 false,说明权限并没有申请,此时需要调用 requestPermission() 方法,主动发送申请权限的操作。

shouldShowRequestPermissionRationale

在调用 requestPermission 方法申请权限之前,需要判断是否需要展示 shouldShowRequestPermissionRationale,该方法会返回以下两种情况:

1)返回 true。用户之前在申请权限操作时,点击了"拒绝"按钮,但是没有选择"Never ask again"选项。

2)返回 false。有两种情况会返回 false

a. 用户从来没有申请过次权限;

b. 用户之前选中拒绝,并且勾选了 "Never ask again"选项。

处理办法

针对返回 true 的情况,很容易处理。这种情况表示用户已经拒绝申请操作,但是并没有选中 "Never ask again" 选项。因此,我们只需要再次调用 requestPermission 方法申请权限即可。系统会自动弹出申请权限的对话框。

对于放回 false 的情况稍微麻烦一点,因为有两种情况会返回 false。针对着两种情况所对应的返回操作也不一样。

如果用户从来没有申请过次权限,那么就同上面返回 true 的情况一样,直接调用 requestPermission() 方法申请权限即可。如果是因为用户之前拒绝申请操作并且勾选了 "Never ask again" 选项,此时我们不应该再执行 requestPermission 方法。通过弹出自定义的对话框,提示用户此操作必须通过权限申请之后才可以继续进行,并给用户提供进入权限设置界面的入口。

注意:shouldShowRequestPermissionRationale 返回 true 的情况,在很多国内厂商的手机中设置了自动屏蔽,也就是没有返回 true 的情况,比如华为、小米手机。

代码演示

接下来以申请通讯录权限为例,来演示如何进行动态权限适配。

首先,判断系统版本是否高于23,代码如下

只有在API版本高于23的系统中,才需要动态申请权限。

在申请之前,还需要检查当前 App 是否已经获取到相应的权限,避免重复申请。如下所示

上图中的 PackageManager.PERMISSION_GRANTED 表示权限已经获取。接下来就是申请权限的流程

上文中已经介绍,在申请权限之前需要调用 shouldShowRequestPermissionRationale 判断用户之前的操作。因此代码修改如下

图中1处,shouldShowRequestPermissionRationale 返回 true,直接调用 requestPermissions 再次申请权限即可。但是,对于返回 false 的情况需要特殊处理,因为有两种情况返回 false。

可以借助于 SharedPreference 判断是否为用户第一次申请权限操作。代码如下所示

上图中使用 SharedPreference 来保存用户是否第一次申请权限的状态值,默认情况为 true。当执行一次权限申请后,调用 firstTimeAsking() 方法将其设置为 false。

权限申请操作封装

App 中会有很多调用危险权限的方法,如果每一次执行这些代码都复制粘贴上述这些权限申请的代码,会显得代码很冗余。因此,可以将动态权限申请的操作封装到工程中的某个 Util 类中,并提供给调用者相应的回调接口。部分核心代码如下

最后,只需要在 BaseActivity 中调用此方法时,传入具体实现的 PermissionRequestListener 即可。如下所示

第三方库使用

对于 Permission 的动态申请,也可以借助于开源的第三方库来加快开发速度。目前,对于 Permission 动态权限申请比较好的开源库有:a)Dexter;b)easypermissions;c)PermissionsDispatcher。

但是,第三方库的使用也具有一定的隐患。不同版本中 Android 系统对 Permission 的处理政策并不完全一致,在新版本的系统中很有可能会添加对权限申请更严格的请求策略。

比如,在 Android 10 中,增加了对外置存储访问的限制。正常情况下,我么可以通过以下代码获取外置存储的根路径,然后在此目录下创建 App 相应的文件缓存数据。

但是,从 Android 10(API 29)开始,App 层没有访问此路径的权限。无论在 AndroidManifest 文件中加上对应的权限,还是使用 ActivityCompact.requestPermissions 动态申请权限,都无法实现访问。目前官方提供的临时解决方案是在 AndroidManifest.xml 清单文件中添加如下设置

但是,如果我么使用的是第三方库处理动态权限申请操作,如果第三方库没有做版本适配,或者做了相应的适配修改,但是并没有升级第三方库的版本。都会造成在 Android 10 设备上处理文件发生异常

总结

本次主要介绍了 Android 系统中申请权限相关的知识点,主要是针对 Android 版本 23 之后的动态申请做了详细介绍。需要掌握的方法:1)checkSelfPermission 检查某权限是否已经申请;2)requestPermissions 主动发送申请权限的请求;3)shouldShowRequestPermissionRationale 判断用户之前对申请权限做出的相应动作。

相关推荐
m0_748235951 小时前
CentOS 7使用RPM安装MySQL
android·mysql·centos
ac-er88885 小时前
Yii框架中的队列:如何实现异步操作
android·开发语言·php
流氓也是种气质 _Cookie7 小时前
uniapp 在线更新应用
android·uniapp
zhangphil9 小时前
Android ValueAnimator ImageView animate() rotation,Kotlin
android·kotlin
徊忆羽菲9 小时前
CentOS7使用源码安装PHP8教程整理
android
编程、小哥哥10 小时前
python操作mysql
android·python
Couvrir洪荒猛兽11 小时前
Android实训十 数据存储和访问
android
五味香13 小时前
Java学习,List 元素替换
android·java·开发语言·python·学习·golang·kotlin
十二测试录13 小时前
【自动化测试】—— Appium使用保姆教程
android·经验分享·测试工具·程序人生·adb·appium·自动化
Couvrir洪荒猛兽15 小时前
Android实训九 数据存储和访问
android