一、运行时权限
所谓运行时权限,指的是在Android6.0及以上版本中,在app运行时才请求相关权限,从而让每项权限都在用户知情的情况下被授权(当然用户可以拒绝)。而不同于Android6.0之前在安装app时告知用户获取全部权限。
关于权限,分为正常权限(一般和设备相关)、危险权限(用户数据相关)和特殊权限。具体可见https://developer.android.com/guide/topics/security/permissions.html?hl=zh-cn#normal-dangerous,其中特殊权限(悬浮窗权限和系统设置)。
二、检查是否具有权限API
private boolean checkPermissions(){
if (Build.VERSION.SDK_INT < 23) {//一般android6以下会在安装时自动获取权限,但在小米机上,可能通过用户权限管理更改权限
return true;
}else {
if (getApplicationInfo().targetSdkVersion < 23) {
//targetSdkVersion<23时 即便运行在android6及以上设备 ContextWrapper.checkSelfPermission和Context.checkSelfPermission失效
//返回值始终为PERMISSION_GRANTED
//此时必须使用PermissionChecker.checkSelfPermission
if (PermissionChecker.checkPermission(this, Manifest.permission.CAMERA, Binder.getCallingPid(), Binder.getCallingUid(), getPackageName()) == PackageManager.PERMISSION_GRANTED) {
return true;
} else {
return false;
}
} else {
if (checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
return true;
} else {
return false;
}
}
}
}
检查是否具有权限主要涉及3个方法:
1、ContextWrapper.checkSelfPermission和Context.checkSelfPermission
只有Build.VERSION.SDK_INT>=23才能调用,且targetSdkVersion>=23结果才有效。因此在targetSdkVersion<23时要用PermissionChecker.checkSelfPermission。
2、PermissionChecker.checkPermission
当在小米4.4上用户手动改变权限后,再次检查权限返回结果有误。适用于Android6.0以上的权限判断。小米4.4需要使用AppOpsManager的权限检验方法。
综上:以上两种,优先选择PermissionChecker.checkPermission。
3、AppOpsManager
为了兼容小米4.4用户可以改变权限的情况,只能使用AppOpsManager。(其实PermissionChecker.checkPermission底层用了Context.checkPermission和AppOpsManager)用法如下:
private boolean checkPermissionForXiaomi(){
AppOpsManager manager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
try {
Method method = manager.getClass().getDeclaredMethod("checkOp", int.class, int.class, String.class);
int property = (Integer) method.invoke(manager, 26,
Binder.getCallingUid(), getPackageName());
if (AppOpsManager.MODE_ALLOWED == property) {
return true;
} else {
return false;
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
三、请求权限API
请求权限主要涉及shouldShowRequestPermissionRationale和requestPermissions方法,用法如下:
if (checkPermissions()) {
Toast.makeText(this,"已经获取权限",Toast.LENGTH_SHORT).show();
} else {
Log.d("haha", "no permission.");
//请求权限
if (Build.VERSION.SDK_INT < 23) return;
if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {//上次请求权限被拒绝
//这里一般自定义一个界面告知用户为何需要此权限
//一般手机还是会弹出用户授权界面,但小米4.4对此作了处理 需通过intent跳转到对应的权限管理界面
//requestPermissions(new String[]{Manifest.permission.CAMERA}, 1);
} else {
requestPermissions(new String[]{Manifest.permission.CAMERA}, 1);
}
}
1、shouldShowRequestPermissionRationale
上一次请求被拒绝则返回true,此时开发者应该有一个页面说明请求权限的原因。
2、requestPermissions
会直接到用户授权界面,注意小米4.4有自己的权限管理设置,这里不会弹出权限界面,需要作特殊处理直接跳转到对应的设置界面。如下:
/**
* 打开权限设置界面
*/
public void openXiaomiSetting() {
try {
Intent localIntent = new Intent(
"miui.intent.action.APP_PERM_EDITOR");
localIntent.setClassName("com.miui.securitycenter",
"com.miui.permcenter.permissions.AppPermissionsEditorActivity");
localIntent.putExtra("extra_pkgname", getPackageName());
startActivityForResult(localIntent, 2);
} catch (ActivityNotFoundException localActivityNotFoundException) {
Intent intent1 = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent1.setData(uri);
startActivityForResult(intent1, 2);
}
}
3、用户授权结果的回调是onRequestPermissionsResult,覆写该方法进行相应处理即可。
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
//权限处理结果的回调
switch (requestCode){
case 1:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED){
Log.d("haha",permissions[0]+" granted.");
Toast.makeText(this,"获取权限成功!",Toast.LENGTH_SHORT).show();
}
break;
case 2:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED){
Log.d("haha",permissions[0]+" granted.");
Toast.makeText(this,"小米获取权限成功!",Toast.LENGTH_SHORT).show();
}
break;
default:
break;
}
}
注:经测试特殊权限只能用AppOpsManager