3、Android权限模型详解:普通权限、危险权限、签名权限、特权权限的定义与区别,权限申请流程与运行时权限机制
说到Android权限,我最早接触的时候也觉得挺绕的。特别是从Android 6.0开始,权限模型经历了一次大手术。今天咱们就把这四种权限掰开揉碎了讲清楚。
3.1 权限的四个等级:从松到严
Android把权限分成了四个等级。说白了,就是根据权限的「危险程度」来分级管理。我习惯把它们想象成四道门,每道门的锁不一样。
| 权限等级 | 定义 | 安装时行为 | 运行时行为 | 典型例子 |
|---|---|---|---|---|
| 普通权限 (Normal) | 不会直接威胁用户隐私或设备安全 | 自动授予 | 无需用户干预 | INTERNET、ACCESS_NETWORK_STATE |
| 危险权限 (Dangerous) | 可能访问用户敏感数据或控制设备 | 不自动授予 | 需要用户弹窗确认 | CAMERA、READ_CONTACTS |
| 签名权限 (Signature) | 仅允许相同签名的应用使用 | 自动授予(同签名) | 无需用户干预 | 系统级内部接口 |
| 特权权限 (Privileged) | 系统预置应用才能使用 | 自动授予(预置应用) | 无需用户干预 | WRITE_SETTINGS、INSTALL_PACKAGES |
嗯,这里要注意:普通权限和危险权限是应用开发者最常打交道的。签名权限和特权权限,说实话,普通应用基本碰不到。
3.2 普通权限:最省心的那类
普通权限,说白了就是那些「就算给了你,也干不了什么坏事」的权限。比如访问网络、设置闹钟、使用蓝牙这些。
在AndroidManifest.xml里声明一下就行:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.SET_ALARM" />
系统会在安装时自动授予,用户甚至不会感知到。我在项目中遇到过一个问题:有个同事把INTERNET权限声明漏了,结果App在模拟器上跑得好好的,真机上一连网就崩溃。排查了半天才发现是权限问题。所以啊,普通权限虽然简单,但漏了也会出大事。
3.3 危险权限:运行时权限机制的核心
危险权限才是重头戏。从Android 6.0开始,Google强制要求危险权限必须在运行时动态申请。为什么?你想想看,以前安装时一股脑全授权,用户根本不知道App在背后干了什么。
危险权限被分成了几个组,我列一下常见的:
- 日历:READ_CALENDAR、WRITE_CALENDAR
- 相机:CAMERA
- 联系人:READ_CONTACTS、WRITE_CONTACTS
- 位置:ACCESS_FINE_LOCATION、ACCESS_COARSE_LOCATION
- 麦克风:RECORD_AUDIO
- 电话:READ_PHONE_STATE、CALL_PHONE
- 传感器:BODY_SENSORS
- 短信:SEND_SMS、READ_SMS
- 存储:READ_EXTERNAL_STORAGE、WRITE_EXTERNAL_STORAGE
这里有个坑:权限组机制。同一个组里的权限,只要用户授权了一个,同组的其他权限也会自动获得授权。比如用户同意了CAMERA,那么同组的其他相机相关权限就不需要再弹窗了。但要注意,这只是行为上的便利,不代表你可以偷懒不声明。
3.4 运行时权限申请流程
我建议你把这个流程记牢,因为几乎每个App都会用到。流程其实就三步:
- 检查权限是否已授予:用ContextCompat.checkSelfPermission()
- 如果未授予,发起请求:用ActivityCompat.requestPermissions()
- 处理回调结果:在onRequestPermissionsResult()里处理
来看一个完整的代码示例,我习惯这么写:
// 第一步:检查权限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
// 第二步:判断是否需要向用户解释为什么需要这个权限
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.CAMERA)) {
// 这里可以弹一个自定义对话框解释原因
showExplanationDialog();
} else {
// 直接请求权限
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.CAMERA},
REQUEST_CAMERA_PERMISSION);
}
} else {
// 权限已授予,直接执行操作
openCamera();
}
// 第三步:处理回调
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_CAMERA_PERMISSION) {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
openCamera();
} else {
// 用户拒绝了权限
Toast.makeText(this, "相机权限被拒绝,无法拍照", Toast.LENGTH_SHORT).show();
}
}
}
shouldShowRequestPermissionRationale这个方法很关键——用户第一次拒绝后,第二次请求前最好解释一下为什么需要这个权限,用户体验会好很多。
3.5 签名权限:自家人的通行证
签名权限,说白了就是「只有跟我用同一个签名密钥签名的App才能用」。这在系统级开发或者同一个公司的应用套件里很常见。
举个例子,我在做车机系统时,有个系统服务需要暴露给特定的系统应用使用。这时候用签名权限就特别合适:
<permission android:name="com.example.car.ACCESS_VEHICLE_DATA"
android:protectionLevel="signature" />
<uses-permission android:name="com.example.car.ACCESS_VEHICLE_DATA" />
只有跟定义这个权限的应用使用相同签名的App,才能获取到这个权限。其他App就算在Manifest里声明了也没用,系统直接忽略。
3.6 特权权限:系统预置应用的专属
特权权限,是Android里最高级别的权限。它只对系统预置应用(放在/system/priv-app目录下的)开放。普通应用就算声明了也没用。
常见的特权权限有:
- WRITE_SETTINGS:修改系统设置
- INSTALL_PACKAGES:安装或卸载应用
- CHANGE_CONFIGURATION:修改系统配置
- REBOOT:重启设备
在车机系统里,特权权限用得比较多。比如车载设置应用需要修改系统音量、亮度等设置,就需要WRITE_SETTINGS权限。但普通第三方导航App,是拿不到这个权限的。
我记得有一次,客户要求第三方App能直接修改系统音量。我解释了半天,说这个权限只有系统预置应用才能用。最后解决方案是:我们提供一个系统级的音量控制服务,通过AIDL接口暴露给第三方App调用。这样既安全又合规。
3.7 避坑指南:我踩过的那些坑
我曾经遇到过一个问题:有个App在Android 9上运行正常,到了Android 10上就闪退。排查了半天,发现是Android 10开始,READ_PHONE_STATE权限被移出了危险权限组,变成了特殊权限。也就是说,不能再用常规的运行时权限申请流程了,需要引导用户去系统设置里手动开启。
所以啊,做Android开发一定要关注每个大版本的权限变化。Google每年都在收紧权限,稍不注意就会踩坑。
另外,还有一个常见问题:权限被用户永久拒绝后怎么办? 如果用户勾选了「不再询问」,那么shouldShowRequestPermissionRationale会返回false,再次调用requestPermissions也不会弹窗。这时候只能引导用户去系统设置里手动开启权限。
// 检测是否被永久拒绝
if (!ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.CAMERA)) {
// 跳转到应用设置页面
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivity(intent);
}
3.8 总结一下
四种权限,各有各的用途。普通权限最省心,危险权限最麻烦,签名权限最安全,特权权限最强大。在实际开发中,我建议你遵循一个原则:最小权限原则——只申请你真正需要的权限,不要贪多。
特别是做车机系统,权限管控比手机更严格。因为车机涉及车辆控制、驾驶安全,一旦权限滥用,后果不堪设想。嗯,今天就先聊到这里,下一章我们讲讲权限的动态管理和最佳实践。