前言
Android6.0(API 23)引入了一种新的权限模式,即运行时应用权限管理。这种模式让用户能够更好地了解和控制权限,用户可为所安装的各个应用分别授予或撤销权限。对于开发者来说意味着在使用对应权限功能时必须要先判断权限是否已经赋予了。
危险权限
对于权限的管理,并不是所有的权限都需要用户允许的,只有一部分危险权限需要许可。可以参照下图
检查、请求权限
- 在AndroidManifest中添加所需的权限
- 在使用危险权限功能前检查权限的状态
checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
- 当没有权限(检查权限状态不为PackageManager.PERMISSION_GRANTED)时请求权限
requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, 1);
- 在用户操作回掉中处理允许和拒绝操作的逻辑
@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) {
Toast.makeText(this, "允许定位权限", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "拒绝定位权限!!!", Toast.LENGTH_SHORT).show();
}
break;
}
}
其中在第三步中requestPermissions()可以一次请求多个权限,对于那些用户已经允许和拒绝并且勾选了不再提示的权限会直接执行回掉,并不会让用户再次选择授权。所以对于那些必须要要权限才能使用的功能可以在用户拒绝之后让用户在设置中手动打开权限。
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.fromParts("package", getPackageName(), null));
startActivity(intent);
对于特殊机型的适配
由于国内很多手机厂商早在Android6.0之前就有了自己的权限管理系统,在Android6.0之后有一些还保留着原有的权限管理系统。这导致了明明用checkSelfPermission()检查有权限但还是还是无法使用权限功能。例如VIVO在所有情况下checkSelfPermission()都是返回有权限,但他在使用到有关权限操作时会自动提示用户权限许可。不过这种情况下调用并不会导致系统报错,而且第三方厂商也做了使用权限的提示。下面以小米为例判断是否在小米中打开权限
/**
*检查是由有权限
**/
private static boolean checkOpsPermission(Context context, String permission) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
try {
AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
String opsName = AppOpsManager.permissionToOp(permission);
if (opsName == null) {
return true;
}
int opsMode = appOpsManager.checkOpNoThrow(opsName, android.os.Process.myUid(), context.getPackageName());
return opsMode == AppOpsManager.MODE_ALLOWED;
} catch (Exception ex) {
return true;
}
} else {
return true;
}
}
/**
* 打开小米权限设置
*/
public void openPermissionSetting(Activity activity) {
String miuiVersion = getMiuiVersion();
Intent intent = null;
if ("V5".equals(miuiVersion)) {
Uri packageURI = Uri.parse("package:" + activity.getApplicationInfo().packageName);
intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, packageURI);
} else if ("V6".equals(miuiVersion) || "V7".equals(miuiVersion)) {
intent = new Intent("miui.intent.action.APP_PERM_EDITOR");
intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity");
intent.putExtra("extra_pkgname", activity.getPackageName());
} else if ("V8".equals(miuiVersion) || "V9".equals(miuiVersion)) {
intent = new Intent("miui.intent.action.APP_PERM_EDITOR");
intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.PermissionsEditorActivity");
intent.putExtra("extra_pkgname", activity.getPackageName());
}
if (null != intent)
activity.startActivity(intent);
}
/**
* 获取MIUI版本
*/
public String getMiuiVersion() {
String line;
BufferedReader input = null;
try {
Process p = Runtime.getRuntime().exec("getprop ro.miui.ui.version.name");
input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024);
line = input.readLine();
input.close();
} catch (IOException ex) {
return null;
} finally {
if (input != null) {
try {
input.close();
} catch (IOException e) {
}
}
}
return line;
}
参考
Android 6.0 运行时权限处理完全解析
权限最佳做法
Android 6.0动态权限及小米(MIUI)权限的特殊处理