Android 6.0 动态权限申请看完就会写
1. 起
Android 的动态权限申请在初学时也是我觉得相对来说比较麻烦的一个点。动态权限申请也并非是从一开始就存在的,而是在 Android 6.0 开始才开始改为动态申请的。
在 6.0 之前只需要在 AndroidManifest.xml 中声明权限即可,当然从 6.0 开始普通权限的获取也只需要这样做就足够,只有部分危险权限才需要搞特殊。
但是稍微总结一下,其实动态权限申请也只需要知道四个方法的用法和意义就可以完全满足基础的使用了,把它们的意义搞明白我觉得是最重要的,思路捋顺了做起来整个流程都显得理所应当了。
2. 权限
先不着急介绍上面提到的四个方法,咱们先从一开始说的普通权限和危险权限来说起,磨刀不误砍柴工。
按 Google 官方文档的说法,权限分为三种:包括安装时权限、运行时权限和特殊权限。
简单讲,安装时权限一般和咱们平时说的普通权限是一回事,在软件安装时就会授予软件该类权限(当然需要已在 AndroidManifest.xml 声明)。
运行时权限就是所谓的危险权限了。这部分权限的申请,除了要在 AndroidManifest.xml 声明之外,还要在运行时动态申请权限。要注意,这里可不能忘记在 xml 文件中声明。
至于最后一个特殊权限我也没看懂,直接引用 Google 文档的原话自己琢磨吧:
特殊权限与特定的应用操作相对应。只有平台和原始设备制造商 (OEM) 可以定义特殊权限。此外,如果平台和 OEM 想要防止有人执行功能特别强大的操作(例如通过其他应用绘图),通常会定义特殊权限。
系统设置中的特殊应用访问权限页面包含一组用户可切换的操作。其中的许多操作都以特殊权限的形式实现。
这里把危险权限列举一下,使用这些权限的时候动态申请就可以了,不在这里面的其他权限直接用就完事了。
权限组名 | 权限名 |
---|---|
CALENDAR | READ_CALENDAR,WRITE_CALENDAR |
CAMERA | CAMERA |
CONTACTS | READ_CONTACTS,WRITE_CONTACTS,GET_ACCOUNTS |
LOCATION | ACCESS_FINE_LOCATION,ACCESS_COARSE_LOCATION |
MICROPHONE | RECORD_AUDIO |
PHONE | READ_PHONE_STATE,CALL_PHONE, READ_CALL_LOG,WRITE_CALL_LOG,ADD_VOICEMAIL,USE_SIP,PROCESS_OUTGOING_CALLS |
SENSORS | BODY_SENSORS |
SMS | SEND_SMS,RECEIVE_SMS,READ_SMS,RECEIVE_WAP_PUSH,RECEIVE_MMS |
STORAGE | READ_EXTERNAL_STORAGE,WRITE_EXTERNAL_STORAGE |
3. 四个方法
ContextCompat.checkSelfPermission
按正常人的思路,申请权限之前应该要先检查是不是已经拥有这个权限。
第一个方法的作用就是检查当前应用是否已经申请到某个危险权限。该方法有两个返回值,如果返回 PackageManager.PERMISSION_GRANTED 则表示已经拥有该权限。若返回 PackageManager.PERMISSION_DENIED 就是未拥有。
ActivityCompat.shouldShowRequestPermissionRationale
执行完第一个方法检查完是否拥有权限后,就可以执行第二个方法。不过这个方法不是必要的,看个人需求,可有可无。
正常情况下这个方法会返回一个 true ,除了两种情况:
一,用户拒绝了权限申请,并且勾选了不再提醒,则该方法返回 false。
二,设备禁止当前应用拥有该权限或是权限未在 AndroidManifest.xml 中声明,则该方法返回 false。
所以我认为该方法的作用就是当用户勾选了不再提醒后,可以有办法去处理,提醒用户进行响应操作手动开启权限。
ActivityCompat.requestPermissions
第三个方法就是真正开始申请想要的权限了,这个看方法名就懂了,不用多说。如果有需要的话第二个方法执行完,判断返回值后进行权限申请即可。不需要的话就跳过第二个方法,第一个方法执行完并判断返回值后直接执行第三个方法。
onRequestPermissionsResult
这是一个复写的方法,复写它并重新根据需求进行实现即可。这个方法的作用是接收第三个方法执行后的返回值,申请成功还是失败可以在这个方法中做对应的处理。下面用代码展示一下这几个方法的用法,直观一点。
4. 使用
- 先在 xml 文件中声明权限
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.apical.kotlinstudy">
//声明所需的权限
<uses-permission android:name="android.permission.WRITE_CALL_LOG" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity">
<intent-filter>
<category android:name="android.intent.category.LAUNCHER"/>
<action android:name="android.intent.action.MAIN"/>
</intent-filter>
</activity>
</application>
</manifest>
- 使用与该权限相关内容前,检查该权限
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_CALL_LOG)
== PackageManager.PERMISSION_GRANTED) {
//已经拥有该权限
//...
} else {
//尚未拥有该权限,去进行申请
requestPermission();
}
- 申请权限
private void requestPermission() {
//上面介绍过了 ActivityCompat.shouldShowRequestPermissionRationale,所以根据需求做对应处理就好了,我这里没有做什么处理
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_CALL_LOG)) {
//PERMISSION_REQUEST_CODE 是代表申请该权限的申请码,随便给一个数字就可以了,不要给重复了就好,待会在下面的复写方法 onRequestPermissionsResult 中还会用到
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_CALL_LOG}, PERMISSION_REQUEST_CODE);
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_CALL_LOG}, PERMISSION_REQUEST_CODE);
}
}
- 在回调方法中处理结果
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == PERMISSION_REQUEST_CODE) {
if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//返回结果为申请成功
//...
} else {
//申请失败
//...
}
}
}
5. 总结
总结起来就四步:
- 第一步,在 AndroidManifest.xml 中声明所需要的权限
- 第二步,调用相关方法前,检查自己有没有这个权限(ContextCompat.checkSelfPermission)
- 第三步,如果没有,就申请这个权限(ActivityCompat.requestPermissions)
- 第四步,申请完在回调中处理申请结果就完事了(onRequestPermissionsResult)
完