Android M 和 N 新特性:运行时权限
在 Android 中,权限系统出于多方面原因,一直是该平台主要的安全隐患之一……
在 Android 中,权限系统出于多方面原因,一直是该平台主要的安全隐患之一:
- 用户只有两种选择:要么在安装时允许应用获得全部权限,却完全不知道应用为何以及何时会使用这些权限;要么拒绝权限,从而根本无法使用该应用(要么全盘接受,要么放弃)。
- 一旦在安装时授予了权限,应用就可以在其整个生命周期内使用该权限而不发出任何通知,除非用户将应用从设备上卸载,否则无法拒绝这些权限。
- 这一机制不仅对用户不透明,对开发者同样如此。您的应用中可能有一个很酷的功能需要某项特定权限,但如果您不向用户说明并解释为何需要它,用户可能会觉得可疑,进而决定不安装您的应用。
- Android 中有超过 130 种不同的权限。即使用户在安装时逐一查看权限,也很快会感到困惑(BODY_SENSORS 到底是什么?BLUETOOTH、BLUETOOTH_ADMIN 和 BLUETOOTH_PRIVILEGED 之间有什么区别?)
Android 权限系统终于迎来重新设计!
从 Android 6.0 Marshmallow 开始,应用在安装时不会被授予任何权限。取而代之的是,应用必须在运行时逐一向用户申请权限。这是保护用户隐私的重要一步:用户可以自行选择应用能访问什么、不能访问什么,同时仍然可以使用该应用。
开发者应当理解新的权限系统,并调整应用以处理所有情况,为用户提供良好的使用体验。
那么作为开发者,关于 Android 新的权限系统,我需要了解哪些内容呢:
1- 权限分为两类:普通权限和危险权限
- 普通权限允许应用访问外部数据,但不包括用户的私人信息。您可以在这里查看完整列表。
- 危险权限允许应用访问用户的私人信息。
2- 对于所有 Android 版本,每当您要使用某项权限(无论是普通权限还是危险权限),都需要在应用的 manifest 中添加相应的
3- 普通权限会被自动授予,您无需在运行时申请。
4- 如果您要访问私人信息,则需要在运行时显式申请对应的“危险权限”,否则相关方法会抛出 SecurityException,导致应用崩溃。
5- 用户可以随时撤销已授予的权限!
6- 权限被划分为若干权限组(Permission Groups),因此如果用户此前已授予某个组中的一项权限,您就可以使用该组中的所有其他权限,而无需再次申请。
7- 要使您的应用适配新的权限系统,您需要遵循以下逻辑:

8- 百闻不如一见,下面我们来看一些代码。在这个示例中,我们将申请 SEND_SMS 权限(参见注释):
//Unique identifier to identify each permission request
final private int REQUEST_CODE_ASK_PERMISSIONS = 999;
private void sendSMSWrapper() {
//Check to see if the permission is available
int hasSendSMSPermission = checkSelfPermission(Manifest.permission.SEND_SMS);
if (hasSendSMSPermission != PackageManager.PERMISSION_GRANTED) {
//if the permission is not granted, check if the user has already denied this request
if (!shouldShowRequestPermissionRationale(Manifest.permission.SEND_SMS)) {
//Explain to the user why we need to send the sms
Snackbar.Make(layout, "Send SMS is required to share the new events with your contacts.", Snackbar.LengthIndefinite)
.SetAction("OK", v => RequestPermissions(Manifest.permission.SEND_SMS, REQUEST_CODE_ASK_PERMISSIONS))
.Show();
return;
}
// request the permission
requestPermissions(new String[] {Manifest.permission.SEND_SMS},
REQUEST_CODE_ASK_PERMISSIONS);
return;
}
// execute your code using the user's private information
sendSMS();
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case REQUEST_CODE_ASK_PERMISSIONS:
{
// Check for SEND_SMS
if (CheckSelfPermission(Manifest.permission.SEND_SMS) == PackageManager.PERMISSION_GRANTED
{
//Permission Granted
sendSMS();
} else {
// Permission Denied
var snack = Snackbar.Make(layout, "Send SMS permission is denied.", Snackbar.LengthShort);
snack.Show();
}
}
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
9- 为了确保向后兼容,我们建议使用 Support Library v4,它为 Android API level < 23 实现了这些新方法。这样您只需编写一套代码,该库会根据 Android 版本返回相应的值。否则,您需要手动检查 Android 版本,对 Android API level < 23 采用旧的权限系统,对更新的 Android API level 采用新的权限系统。
if (Build.VERSION.SDK_INT >= 23) {
// Runtime permission system
} else {
// Old permission system
}
10- 理解 Android 新的权限系统对于为用户提供更好的使用体验非常重要。不过,为了让代码更简短、更整洁,您也可以借助第三方库来处理权限。我们最喜欢的是 PermissionsDispatcher,它提供了基于注解的简洁 API,并涵盖了所有基本用法。
11- 要进一步了解新的权限系统,您可以参阅以下参考资料:
- 声明权限(Declaring Permissions)
- 使用权限(Using Permissions)
- 权限最佳实践(Permissions Best Practices)
- Android 全部权限列表
- Android Marshmallow 6.0:申请权限(Asking For Permission)