一、简介:
在android6.0以后,我们可以直接安装应用,当app需要我们授予不恰当的权限的时候,我们可以予以拒绝。当然你也可以在设置界面对每个app的权限进行查看,以及对单个权限进行授权或者解除授权。
新的权限机制更好的保护了用户的隐私,Google将权限分为两类,一类是Normal Permissions,这类权限一般不涉及用户隐私,是不需要用户进行授权的,比如手机震动、访问网络等;另一类是Dangerous Permission,一般是涉及到用户隐私的,需要用户进行授权,比如读取sdcard、访问通讯录等。
Dangerous Permissions:
group:com.google.android.gms.permission.CAR_INFORMATION
permission:com.google.android.gms.permission.CAR_VENDOR_EXTENSION
permission:com.google.android.gms.permission.CAR_MILEAGE
permission:com.google.android.gms.permission.CAR_FUEL
group:android.permission-group.CONTACTS
permission:android.permission.WRITE_CONTACTS
permission:android.permission.GET_ACCOUNTS
permission:android.permission.READ_CONTACTS
group:android.permission-group.PHONE
permission:android.permission.READ_CALL_LOG
permission:android.permission.READ_PHONE_STATE
permission:android.permission.CALL_PHONE
permission:android.permission.WRITE_CALL_LOG
permission:android.permission.USE_SIP
permission:android.permission.PROCESS_OUTGOING_CALLS
permission:com.android.voicemail.permission.ADD_VOICEMAIL
group:android.permission-group.CALENDAR
permission:android.permission.READ_CALENDAR
permission:android.permission.WRITE_CALENDAR
group:android.permission-group.CAMERA
permission:android.permission.CAMERA
group:android.permission-group.SENSORS
permission:android.permission.BODY_SENSORS
group:android.permission-group.LOCATION
permission:android.permission.ACCESS_FINE_LOCATION
permission:com.google.android.gms.permission.CAR_SPEED
permission:android.permission.ACCESS_COARSE_LOCATION
group:android.permission-group.STORAGE
permission:android.permission.READ_EXTERNAL_STORAGE
permission:android.permission.WRITE_EXTERNAL_STORAGE
group:android.permission-group.MICROPHONE
permission:android.permission.RECORD_AUDIO
group:android.permission-group.SMS
permission:android.permission.READ_SMS
permission:android.permission.RECEIVE_WAP_PUSH
permission:android.permission.RECEIVE_MMS
permission:android.permission.RECEIVE_SMS
permission:android.permission.SEND_SMS
permission:android.permission.READ_CELL_BROADCASTS
可以使用命令adb shell pm list permissions -d -g来查看危险权限;可以看到危险全是都是一组一组的。只要你授权过某组的一个权限,当需要用到该组其他权限的时候不需要再申请,系统会立即授权。比如你的app对READ_CONTACTS已经授权了,当你的app申请WRITE_CONTACTS时,系统会直接授权通过。此外,对于申请时弹出的dialog上面的文本说明也是对整个权限组的说明,而不是单个权限(ps:这个dialog是不能进行定制的)。
但是不能过多依赖权限组,如果应用需要一项危险权限,那么每次执行需要该权限的操作时,您都必须检查自己是否具有该权限(尽可能对每个危险权限都进行正常的流程申请,因为在后期的版本中这个权限组可能会产生变化)。
二、权限适配流程:
1、向清单中添加权限:
对于所有 Android 版本,要声明应用需要某项权限,请在应用清单中添加 元素,作为顶级 元素的子项,如下
2、检查权限
permission权限说明
如果应用需要一项危险权限,那么每次执行需要该权限的操作时,您都必须检查自己是否具有该权限。要检查您是否具有某项权限,调用ContextCompat.checkSelfPermission() 方法。例如以下代码段展示了如何检查Activity中是否具有允许程序读取用户联系人数据的权限:
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)!= PackageManager.PERMISSION_GRANTED)
{
//权限未授予
}
如果应用具有此权限,该方法将返回 PERMISSION_GRANTED,并且应用可以继续操作。如果应用不具备此权限,该方法将返回 PERMISSION_DENIED,且应用必须明确要求用户授予权限。
3,请求权限
当您的应用从 checkSelfPermission() 收到 PERMISSION_DENIED 时,您需要提示用户授予该权限。Android 为您提供了几种可用来请求权限的方法(如 requestPermissions()),如下面的代码段所示。调用这些方法时,会显示一个无法自定义的标准 Android 对话框。
ActivityCompat.requestPermissions(thisActivity,new String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
该方法是异步的,第一个参数是Context;第二个参数是需要申请的权限的字符串数组;第三个参数为requestCode,主要用于回调的时候检测。可以从方法名requestPermissions以及第二个参数看出,是支持一次性申请多个权限的,系统会通过对话框逐一询问用户是否授权。
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)!= PackageManager.PERMISSION_GRANTED)
{
//权限未授予,请求授予权限
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.READ_CONTACTS},1);
}
系统显示的提示说明了您的应用需要访问的权限组,而不是具体权限。
4、处理权限请求响应
当用户响应您应用的权限请求时,系统会调用您应用的 onRequestPermissionsResult() 方法,在调用过程中向其传递用户响应。您的应用必须替换该方法,以查明是否已向其授予相应权限。在回调过程中传递的请求代码与传递给 requestPermissions() 的请求代码相同。
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch(requestCode)
{
case 1: //请求码为1,代表
if (grantResults.length>0&&grantResults[0]==PackageManager.PERMISSION_GRANTED)
{
//授予了权限
//您需要执行的与联系人相关的任务。
readContacts();
}
else
{
//权限被拒绝,禁用取决于此权限的功能。
Toast.makeText(this,"You denied the permission",Toast.LENGTH_SHORT).show();
}
break;
default:
}
}
对于权限的申请结果,首先验证requestCode定位到你的申请,然后验证grantResults对应于申请的结果,这里的数组对应于申请时的第二个权限字符串数组。如果你同时申请两个权限,那么grantResults的length就为2,分别记录你两个权限的申请结果。
5、shouldShowRequestPermissionRationale()
在某些情况下,您需要帮助用户理解为什么您的应用需要某项权限,可以在调用requestPermission方法前使用Toast提供一些信息;也可以使用的一种方法是只在用户之前拒绝过该权限请求的情况下才提供解释,Android 提供了一种实用程序方法,即 shouldShowRequestPermissionRationale()。如果用户之前拒绝了该请求,该方法将返回 true;如果用户之前拒绝了某项权限并且选中了权限请求对话框中的不再询问选项,或者如果设备政策禁止该权限,该方法将返回 false。
应用安装后第一次访问,直接返回false;
1、第一次请求权限时,用户拒绝了,下一次shouldShowRequestPermissionRationale()返回 true,这时候可以显示一些为什么需要这个权限的说明;
2、第二次请求权限时,用户拒绝了,并选择了“不再提醒”的选项shouldShowRequestPermissionRationale()返回 false;
3、设备的系统设置中禁止当前应用获取这个权限的授权,shouldShowRequestPermissionRationale()返回false;
注意:第二次请求权限时,才会有“不再提醒”的选项,如果用户一直拒绝,并没有选择“不再提醒”的选项,下次请求权限时,会继续有“不再提醒”的选项,并且shouldShowRequestPermissionRationale()也会一直返回true。
并且建议在回调的时候调用,因为我试过了,只有在回调时调用才能达到上述效果,如下:
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch(requestCode)
{
case 1:
if (grantResults.length>0&&grantResults[0]==PackageManager.PERMISSION_GRANTED)
{
readContacts();
}
else //else表示没有获得权限,所以不需要再判断是否获得权限了
{
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_CONTACTS)) {
Toast.makeText(this,"我们需要获得READ_CONTACTS权限才可以访问通讯录!!!",Toast.LENGTH_SHORT).show();
// *异步*向用户显示说明-不要阻止
//此线程等待用户的响应!之后用户
//看到说明,然后重试以请求许可。
} else {
// 无需解释;请求许可
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_CONTACTS},1);
}
}
break;
default:
}
}
参考资料:
1、官方文档—请求应用权限
2、Android 6.0 运行时权限处理完全解析
3、Android 6.0 Permission权限与安全机制