Android 6.0+ 权限申请
前言
安卓系统权限(Runtime Permissions)分为两种:普通权限和高危权限。对于普通权限的申请,只在APP安装的时候询问一次,而对于高危权限,从Android M(API.23)开始,将会在APP运行时动态申请,这样就可以使用户选择是否授予APP该权限,从而保护用户安全。
运行时权限官方文档解释:https://developer.android.com/training/permissions/requesting.html
注:
- 如果设备系统低于6.0,权限只会在APP安装时询问
- 如果targetSdkVersion小于23,权限只会在APP安装时询问
需要动态申请的权限包括以下几种。
注意:同一个组内一个权限被授权了,同组内其他权限也自动被授权
Permission Group | Permissions |
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 |
有图有真相
运行时的权限申请
- 首先判断权限是否已经被申请
- 如果没有申请该权限,则申请权限
- 第一次申请被拒绝(用户也未点击不再询问),如果再次申请权限,需要给用户提示,为什么需要申请该权限,并在用户点击允许后,申请该权限
1.权限判断
int checkSelfPermission = ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA);
if (checkSelfPermission != PackageManager.PERMISSION_GRANTED) {
// 没有权限,需要申请
} else {
// 有权限了
}
2.权限申请
ActivityCompat.requestPermissions(MainActivity.this,new String[] { Manifest.permission.CAMERA }, REQUEST_STORAGE_PERMISSION);
申请权限的时候,第二个参数为数组格式,也就是说,可以同时申请多个权限,但不建议这么做。REQUEST_STORAGE_PERMISSION为自定义的请求码。
如果在Fragment中申请权限的话,需要调用Fragment的
requestPermissions(...)
方法,把ActivityCompat.requestPermissions
改为requestPermissions
。否则会回调到Activity的 onRequestPermissionsResult方法。
3.处理权限申请回调-onRequestPermissionsResult
权限申请的成功或失败之后的操作需要在此回调方法中实现。
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
switch (requestCode) {
case REQUEST_STORAGE_PERMISSION:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 权限获得成功
} else {
// 权限没有获得
}
break;
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
- 因为权限可以同时申请多个,所以回调结果也是以数组的方式返回。此处我们以只请求一个权限为例,如果用户授予权限,则
grantResults[0] = PackageManager.PERMISSION_GRANTED;
4.shouldShowRequestPermissionRationale(…)
1.第一次申请权限的时候,该方法返回false
2.第一次申请权限失败,用户点击拒绝授权后,如果再次申请权限,则该方法返回true
3.如果用户拒绝授权并选择[不再提醒]之后,该方法返回false
4.设备系统禁止APP使用该权限后,返回false
用户拒绝授权权限的时候,系统会提供一个方法让我们解释为什么要申请该权限。因此,我们可以显示一个Dialog向用户再次申请权限。
if (checkSelfPermission != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) {
//向用户解释权限申请原因
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("二维码扫描需要使用相机权限");
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
// 权限申请
}
});
builder.setNegativeButton("Cancle", null);
builder.show();
} else {
// 权限申请
}
} else {
// 有权限了
}
第三方库 EasyPermissions
EasyPermissions简化了Android运行时权限申请、判断、处理的操作步骤
官方文档 https://github.com/googlesamples/easypermissions
1.build.gradle添加依赖
dependencies {
compile 'pub.devrel:easypermissions:0.3.0'
注:如果提示Error:Failed to resolve: com.android.support:appcompat-v7:25.1.0
,就是需要你升级你的SDK了。
2.检查权限
String[] perms = {Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE};
if (!EasyPermissions.hasPermissions(this, perms)) {
// 没有权限
}
3.申请权限
@AfterPermissionGranted(REQUEST_CODE_QRCODE_PERMISSIONS)
private void requestCodeQRCodePermissions() {
String[] perms = {Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE};
if (!EasyPermissions.hasPermissions(this, perms)) {
EasyPermissions.requestPermissions(this, "扫描二维码需要打开相机和散光灯的权限", REQUEST_CODE_QRCODE_PERMISSIONS, perms);
}else {
// 权限已经有了,可以进行其他操作
}
}
在第一次申请失败后,如果需要再次申请,会自动弹出Dialog对话框,第二个参数即为对话框的Message,向用户解释为什么申请该权限,提高用户授权的可能性。
AfterPermissionGranted是一个可选的注解,如果有此标签,那么当request值对应的权限申请通过的话会自动调用该方法。
4.实现EasyPermissions.PermissionCallbacks接口,直接处理权限申请的成功或失败
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// Forward results to EasyPermissions
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}
@Override
public void onPermissionsGranted(int requestCode, List<String> perms) {
// 权限申请成功
}
@Override
public void onPermissionsDenied(int requestCode, List<String> perms) {
// 权限申请失败
}
5.用户点击“不在询问”
如果用户点击了”不在询问“,就无法在通过APP获取权限了,权限只能在设置界面重新授予。这种情况下,你可以使用EasyPermissions.somePermissionPermanentlyDenied(...)
方法来显示一个系统对话框,用以打开系统设置界面。
@Override
public void onPermissionsDenied(int requestCode, List<String> perms) {
// 权限申请失败
if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {
new AppSettingsDialog.Builder(this).build().show();
}
}
当用户从设置界面返回到APP界面的时候,在onActivityResult(...)
方法中就可以再次判断权限的授权情况了。
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == AppSettingsDialog.DEFAULT_SETTINGS_REQ_CODE){
String[] perms = {Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE};
if (EasyPermissions.hasPermissions(this, perms)) {
Toast.makeText(MainActivity.this,"权限被用户通过设置界面重新授予了",Toast.LENGTH_SHORT).show();
}
}
}