作者简介 原创微信公众号郭霖 WeChat ID: guolin_blog
本篇是钊林的第二篇投稿,示例对比了清单权限与运行时权限,并给出了运行时权限的适配方案。希望能够帮助到大家。
钊林的博客地址:
http://teachcourse.cn
正文
摘要
为什么系统禁用录音权限后,在 Android 6.0以上 版本手机运行崩溃?为什么清单文件声明了录音权限,Android 6.0以下 版本仅第一次提示权限授予窗口?为什么使用运行时权限请求,返回权限以授予?怎么让Android应用程序在每次操作时识别系统是否禁用对应权限?如果你和我一样存在很多很多的疑问,说明你还没明白传统的manifest清单文件声明权限和运行时权限请求之间的区别。
对比传统权限声明和运行时权限请求的区别
传统权限声明针对 Android 6.0 及其以下版本使用,Android 6.0对应的API版本23,声明的方式直接将所有应用程序用到的权限统一在 manifest 清单文件中定义,使用标签,应用程序点击安装的过程,罗列清单文件声明的所有权限,安装完成后用户可以选择是否授予应用程序某个隐私的权限,Android系统提供:允许、提示和禁止三种选择,下面看一组演示:
build.gradle 选择编译版本、目标版本都是 API 19,运行在Android 4.4.2系统(华为)效果:
图 1
build.gradle 选择编译版本、目标版本都是 API 19,运行在Android 6.0.1系统(小米)效果
图 2
build.gradle选择编译版本、目标版本都是API 23,运行在Android 4.4.2系统(华为)效果:
图 3
build.gradle选择编译版本、目标版本都是API 23,运行在Android 6.0.1系统(小米)效果:
图 4
图1演示传统权限授予过程,在完成安装的过程中可以选择某个权限是否允许、提示和禁止状态;
图2演示低版本应用程序在Android 6.0以上系统安装过程,默认授予应用程序清单文件声明的所有权限,小米手机测试无法修改权限状态;
图3和图4演示API版本23开发的应用程序分别安装在低版本和高版本系统权限授予过程,安装在低版本时授予权限过程和传统的方式一样,用户可以修改权限的状态;安装到高版本时授予权限过程发生了很大变化,用户安装过程无法修改权限状态,最后运行应用程序的录音功能,出现闪退、崩溃现象。到这里,你是不是和我一样,有一点点明白传统权限声明和运行时权限请求之间的区别吗?
深入理解运行时权限请求过程
是不是我们可以大致认为:使用 API 23 及其以上版本开发的应用程序安装在Android 6.0系统以下的手机,默认授予应用程序清单文件所有的权限,安装在Android 6.0系统以上的手机默认禁止清单文件声明的所有权限?应用程序获取权限的过程中,调用Android开发库提供的一些方法,某个方法返回null或属性为null,就可能导致使用部分功能时应用程序崩溃,而部分被禁止权限的功能虽然不会导致程序崩溃,但也无法获取正确的数值。
运行时权限的出现,一改传统清单文件一键授权的不足,防止用户安装过程的惯性操作,获取了用户某些隐私权限,这些权限包括:收集位置信息,读取短信内容,记录用户数据等,然后进行一些非法操作:发送短信订阅资费套餐,扣取手机话费等,为了用户隐私信息的安全,API 23开发的应用程序统一在运行时提醒用户授予权限,仅授予针对当前功能使用到的权限,未使用到的权限默认禁止。
那么如何兼容低版本的应用程序呢?以及如何让高版本的应用程序也能在Android 6.0以下系统正常运行?那可能就像文章开头演示的四种效果图。
运行时权限涉及的几个过程:第一检查权限是否被授予,使用方法 checkPermission();第二请求获取权限,使用方法 requestPermissions();第三用户是否授予应用程序权限,监听回调方法 onRequestPermissionsResult(),为了防止应用API 23开发的应用程序在Android 6.0以上系统正常安装,在代码中添加权限检查,如下:
针对图4,运行时请求获取录音权限,然后点击禁止后回调方法 onRequestPermissionsResult(),如下图:
应用程序请求授予权限后,如果用户点击禁止,以后每次权限检查不再出现选择提示窗口,onRequestPermissionsResult() 方法返回权限的状态是 PackageManager.PERMISSION_DENIED,防止反复弹出要求用户授予权限弹窗,如果开发者仍然期待在用户没有禁止权限状态后,再次提醒用户授予权限,需要调用方法 shouldShowRequestPermissionRationale(),该方法的目的显示系统UI说明提示用户重新授予应用程序权限,Nexus 5 测试运行效果,如下:
动图
查看 shouldShowRequestPermissionRationale() 源码说明,详细了解该方法的使用:获取是否你应该通过显示UI说明请求授权的原因,只有当你没有获得该权限,同时当前上下文环境需要的权限没有明确和用户沟通——对于获取该权限有什么用处,这时候你应该调用该方法。比如说,如果你写了一个拍照功能的APP,请求了用户可能需要的拍照权限,而没有解释为什么请求的权限是必须的,可能用户没觉得不正常;然而如果当前APP在拍照时请求获取位置的权限,这时对于一个不精通技术的用户来说可能想知道定位和拍照是怎样的一种联系。在这个情景之下,你大概会选择通过一个显示UI说明请求授权的原因:
说明:在Android 6.0.1系统的小米手机测试,在用户禁止后,调用 shouldShowRequestPermissionRationale 该方法没有显示说明;使用 Android Studio 内置的Nexus 5模拟器测试用户第一次请求权限弹窗只有 DENY 和 ALLOW 选项,在用户选择 DENY 后再次调用 requestPermissions方法,弹窗除了 DENY 和 ALLOW 选项外,还多了一个 Never ask again 复选框。
关于ActivityCompat的说明
在上面检查授予权限的代码中,我使用了 getPackageManager().checkPermission() 这个方法检查,考虑到兼容高低版本API的问题,还是推荐使用v4包下的 ActivityCompat.checkSelfPermission() 这个静态方法或者父类 ContextCompat.checkSelfPermission();请求权限推荐使用 ActivityCompat.requestPermissions() 这个静态方法,如果第一次禁止后,重新弹窗显示UI说明,调用静态方法 ActivityCompat.shouldShowRequestPermissionRationale() 后重新授权,具体可以查看 ActivityCompat 源码理解它们之间的关系。
在API 23的版本中,查看 ActivityCompat 的源码,上述的三个方法最终来自受保护的类 ActivityCompatApi23,在源码中检查了应用程序的API版本。
响应用户授权状态的回调方法 onRequestPermissionsResult() 属于 ActivityCompat 内部的一个接口,如果没有猜错的话,仅在API 23以后的版本中,实现了 ActivityCompat.OnRequestPermissionsResultCallback接口 的Activity子类,才能回调 onRequestPermissionsResult()方法,同时也会看到 FragmentActivity、AppCompatActivity 源码实现了上述接口。
运行时权限策略
提出了运行时权限,在运行应用程序的时候,每使用应用程序的一个功能开发者就需要请求授权一次,那必然会加大了开发者的工作量,请求权限的代码会变得很多,同时本来运行时权限的申请方式本来就比传统权限请求方式复杂,如果再让开发者一次次请求授权那肯定非常反感。为了解决权限反复多次请求的问题,Google采用了权限分组的策略:同一组的多个权限,只要获得了用户授予的一个权限,同时可以使用同组的其他权限,权限的分组情况如下图:
android.permission-group.CALENDAR
android.permission.READ_CALENDAR
android.permission.WRITE_CALENDAR
android.permission-group.CAMERA
android.permission.CAMERA
android.permission-group.CONTACTS
android.permission.READ_CONTACTS
android.permission.WRITE_CONTACTS
android.permission.GET_GET_ACCOUNTS
android.permission-group.LOCATION
android.permission.ACCESS_COARSE_LOCATION
android.permission.ACCESS_FINE_LOCATION
android.permission-group.MICROPHONE
android.permission.RECORD_AUDIO
android.permission-group.PHONE
android.permission.READ_PHONE_STATE
android.permission.CALL_PHONE
android.permission.READ_CALL_LOG
android.permission.WRITE_CALL_LOG
android.permission.ADD_VOICEMAIL
android.permission.USE_SIP
android.permission.PROCESS_OUTGOING_CALLS
android.permission-group.SENSORS
android.permission.BODY_SENSORS
android.permission-group.SMS
android.permission.SEND_SMS
android.permission.RECEIVE_SMS
android.permission.READ_SMS
android.permission.RECEIVE_WAP_PUSH
android.permission.RECEIVE_MMS
android.permission-group.STORAGE
android.permission.READ_EXTERNAL_STORAGE
android.permission.WRITE_EXTERNAL_STORAGE
参考资料:
https://developer.android.google.cn/guide/topics/security/permissions.html#perm-groups
完。。。。。。。。。。。。。。。。。。。。。
文章原创作者GuoLin 书籍推荐
郭林大神原创android 书籍:《第一行代码 android》