Appearance
Android 权限系统深度解析
目录
1. 权限系统概述
1.1 权限系统架构
Android 权限系统架构:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 应用层 │
│ ├─ 声明权限(AndroidManifest.xml) │
│ ├─ 请求权限(运行时) │
│ └─ 检查权限(代码中) │
│ ↓ │
│ 框架层 │
│ ├─ PackageManagerService 管理权限 │
│ ├─ ActivityManagerService 处理权限请求 │
│ └─ PermissionController 控制权限 │
│ ↓ │
│ 系统层 │
│ ├─ SELinux 强制访问控制 │
│ ├─ 沙箱隔离 │
│ └─ 内核权限检查 │
│ │
└─────────────────────────────────────────────────────────────┘1.2 权限发展历程
Android 权限系统演进:
┌─────────────────────────────────────────────────────────────┐
│ │
│ Android 5.1 及之前 │
│ ├─ 安装时授权 │
│ ├─ 全有或全无 │
│ ├─ 用户无法选择权限 │
│ └─ 权限一次性授予 │
│ │
│ Android 6.0 (M) │
│ ├─ 运行时权限引入 │
│ ├─ 危险权限需要运行时请求 │
│ ├─ 用户可选择性授权 │
│ └─ 权限可动态撤销 │
│ │
│ Android 7.0 (N) │
│ ├─ 权限组优化 │
│ ├─ 权限请求优化 │
│ └─ 后台权限限制开始 │
│ │
│ Android 8.0 (O) │
│ ├─ 后台启动限制 │
│ ├─ 精确位置权限 │
│ └─ 通知权限细化 │
│ │
│ Android 9.0 (Pie) │
│ ├─ 分区增强 │
│ ├─ 后台位置限制 │
│ └─ 权限使用限制 │
│ │
│ Android 10 (Q) │
│ ├─ 模糊位置权限 │
│ ├─ 后台权限限制加强 │
│ ├─ 分区存储 │
│ └─ 权限自动授予优化 │
│ │
│ Android 11 (R) │
│ ├─ 一次性权限 │
│ ├─ 后台权限限制进一步 │
│ ├─ 权限使用情况显示 │
│ └─ 通知权限分类 │
│ │
│ Android 12 (S) │
│ ├─ 通知权限强制请求 │
│ ├─ 权限请求优化 │
│ ├─ 权限使用情况报告 │
│ └─ 后台权限限制更严格 │
│ │
│ Android 13 (T) │
│ ├─ 媒体权限拆分 │
│ ├─ 通知权限正式引入 │
│ ├─ 短信权限细化 │
│ └─ 通话权限细化 │
│ │
│ Android 14 (U) │
│ ├─ 权限请求优化 │
│ ├─ 权限使用追踪 │
│ ├─ 权限管理改进 │
│ └─ 隐私权限增强 │
│ │
└─────────────────────────────────────────────────────────────┘2. 权限级别详解
2.1 普通权限 (Normal)
普通权限特点:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 特点 │
│ ├─ 不影响用户隐私 │
│ ├─ 安装时自动授予 │
│ ├─ 不需要用户确认 │
│ └─ 不需要运行时请求 │
│ │
│ 示例权限 │
│ ├─ INTERNET # 网络访问 │
│ ├─ ACCESS_NETWORK_STATE # 网络状态 │
│ ├─ VIBRATE # 振动 │
│ ├─ WRITE_SETTINGS # 写入设置 (实际是 signature) │
│ ├─ RECEIVE_BOOT_COMPLETED # 开机广播 │
│ ├─ FOREGROUND_SERVICE # 前台服务 │
│ └─ POST_NOTIFICATIONS # 发送通知 (Android 13+ 需请求) │
│ │
└─────────────────────────────────────────────────────────────┘2.2 危险权限 (Dangerous)
危险权限分类:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 相机权限 │
│ ├─ CAMERA # 访问相机 │
│ └─ 涉及用户隐私,需要运行时请求 │
│ │
│ 位置权限 │
│ ├─ ACCESS_FINE_LOCATION # 精确位置 │
│ ├─ ACCESS_COARSE_LOCATION # 模糊位置 │
│ └─ 涉及用户位置隐私 │
│ │
│ 通讯录权限 │
│ ├─ READ_CONTACTS # 读取通讯录 │
│ ├─ WRITE_CONTACTS # 写入通讯录 │
│ └─ 涉及用户联系人隐私 │
│ │
│ 日历权限 │
│ ├─ READ_CALENDAR # 读取日历 │
│ ├─ WRITE_CALENDAR # 写入日历 │
│ └─ 涉及用户日程隐私 │
│ │
│ 电话权限 │
│ ├─ CALL_PHONE # 拨打电话 │
│ ├─ READ_PHONE_STATE # 读取电话状态 │
│ ├─ READ_PHONE_NUMBERS # 读取电话号码 │
│ ├─ READ_CALL_LOG # 读取通话记录 │
│ ├─ WRITE_CALL_LOG # 写入通话记录 │
│ └─ 涉及用户通话隐私 │
│ │
│ 短信权限 │
│ ├─ SEND_SMS # 发送短信 │
│ ├─ RECEIVE_SMS # 接收短信 │
│ ├─ READ_SMS # 读取短信 │
│ └─ 涉及用户短信隐私 │
│ │
│ 存储权限 │
│ ├─ READ_EXTERNAL_STORAGE # 读取外部存储 │
│ ├─ WRITE_EXTERNAL_STORAGE # 写入外部存储 │
│ ├─ MANAGE_EXTERNAL_STORAGE # 管理所有文件 (Android 11+) │
│ └─ 涉及用户文件隐私 │
│ │
│ 传感器权限 │
│ ├─ BODY_SENSORS # 身体传感器 │
│ └─ 涉及用户健康数据 │
│ │
└─────────────────────────────────────────────────────────────┘2.3 签名权限 (Signature)
签名权限特点:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 特点 │
│ ├─ 仅限系统签名应用 │
│ ├─ 安装时自动授予(如果签名匹配) │
│ ├─ 普通应用无法获得 │
│ └─ 用于系统级功能 │
│ │
│ 示例权限 │
│ ├─ REBOOT # 重启系统 │
│ ├─ SET_DEBUG_APP # 设置调试应用 │
│ ├─ BIND_ACCESSIBILITY_SERVICE # 绑定无障碍服务 │
│ ├─ BIND_INPUT_METHOD # 绑定输入法 │
│ ├─ WRITE_SETTINGS # 写入系统设置 │
│ ├─ DELETE_PACKAGES # 删除应用 │
│ └─ GRANT_PERMISSIONS # 授予权限 │
│ │
└─────────────────────────────────────────────────────────────┘2.4 签名或系统权限 (Signature or System)
签名或系统权限:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 特点 │
│ ├─ 系统应用或签名应用可获得 │
│ ├─ 系统分区应用自动授予 │
│ ├─ 普通应用无法获得 │
│ └─ 用于系统级功能 │
│ │
│ 示例权限 │
│ ├─ BIND_TELEphony_SERVICE # 绑定电话服务 │
│ ├─ BROADCAST_STICKY # 发送粘性广播 │
│ ├─ PROCESS_OUTGOING_CALLS # 处理拨出通话 │
│ └─ READ_LOGS # 读取日志 │
│ │
└─────────────────────────────────────────────────────────────┘3. 运行时权限 (Android 6.0+)
3.1 运行时权限请求流程
运行时权限请求流程:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 应用检查权限状态 │
│ ↓ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 检查是否已授予 │ │
│ │ - ContextCompat.checkSelfPermission() │ │
│ └─────────────────────────────────────────────────┘ │
│ ↓ │
│ 未授予则请求权限 │
│ ↓ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Activity.requestPermissions() │ │
│ │ - 显示系统权限对话框 │ │
│ │ - 用户选择允许或拒绝 │ │
│ └──────────────────────────────────────────┘ │
│ ↓ │
│ 用户做出选择 │
│ ↓ │
│ ┌──────────────────────────────────────────┐ │
│ │ 回调 onRequestPermissionsResult() │ │
│ │ - 处理允许结果 │ │
│ │ - 处理拒绝结果 │ │
│ └──────────────────────────────────────────┘ │
│ ↓ │
│ 执行相应操作 │
│ │
└─────────────────────────────────────────────────────────────┘3.2 运行时权限请求代码
基本权限请求:
java
// 运行时权限请求
public class PermissionActivity extends AppCompatActivity {
private static final int PERMISSION_REQUEST_CODE = 100;
// 请求相机权限
public void requestCameraPermission() {
// 1. 检查权限
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
// 2. 检查是否需要说明
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.CAMERA)) {
// 显示说明对话框
showPermissionRationaleDialog();
}
// 3. 请求权限
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.CAMERA},
PERMISSION_REQUEST_CODE);
} else {
// 权限已授予,执行操作
useCamera();
}
}
// 处理权限结果
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PERMISSION_REQUEST_CODE) {
if (grantResults.length > 0 &&
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 权限已授予
useCamera();
} else {
// 权限被拒绝
showPermissionDeniedDialog();
}
}
}
}3.3 多权限请求
请求多个权限:
java
// 请求多个权限
public void requestMultiplePermissions() {
// 1. 检查需要请求的权限
List<String> permissionsToRequest = new ArrayList<>();
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
permissionsToRequest.add(Manifest.permission.CAMERA);
}
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.RECORD_AUDIO)
!= PackageManager.PERMISSION_GRANTED) {
permissionsToRequest.add(Manifest.permission.RECORD_AUDIO);
}
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
permissionsToRequest.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
// 2. 如果有需要请求的权限
if (!permissionsToRequest.isEmpty()) {
ActivityCompat.requestPermissions(this,
permissionsToRequest.toArray(new String[0]),
PERMISSION_REQUEST_CODE);
} else {
// 所有权限都已授予
usePermissions();
}
}
// 处理多权限结果
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PERMISSION_REQUEST_CODE) {
boolean allGranted = true;
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
allGranted = false;
break;
}
}
if (allGranted) {
usePermissions();
} else {
showPartialPermissionDeniedDialog();
}
}
}4. 权限组(Permission Groups)
4.1 权限组分类
权限组分类:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 相机组 (CAMERA) │
│ └─ CAMERA │
│ │
│ 位置组 (LOCATION) │
│ ├─ ACCESS_FINE_LOCATION │
│ └─ ACCESS_COARSE_LOCATION │
│ │
│ 通讯录组 (CONTACTS) │
│ ├─ READ_CONTACTS │
│ └─ WRITE_CONTACTS │
│ │
│ 日历组 (CALENDAR) │
│ ├─ READ_CALENDAR │
│ └─ WRITE_CALENDAR │
│ │
│ 短信组 (SMS) │
│ ├─ SEND_SMS │
│ ├─ RECEIVE_SMS │
│ └─ READ_SMS │
│ │
│ 电话组 (PHONE) │
│ ├─ CALL_PHONE │
│ ├─ READ_PHONE_STATE │
│ ├─ READ_PHONE_NUMBERS │
│ ├─ READ_CALL_LOG │
│ └─ WRITE_CALL_LOG │
│ │
│ 存储组 (STORAGE) │
│ ├─ READ_EXTERNAL_STORAGE │
│ └─ WRITE_EXTERNAL_STORAGE │
│ │
│ 传感器组 (SENSORS) │
│ └─ BODY_SENSORS │
│ │
└─────────────────────────────────────────────────────────────┘4.2 权限组优势
权限组优势:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 1. 一次性授予组内所有权限 │
│ ├─ 减少权限请求次数 │
│ ├─ 提升用户体验 │
│ └─ 简化权限管理 │
│ │
│ 2. 权限关联 │
│ ├─ 组内权限相关功能 │
│ ├─ 统一管理 │
│ └─ 便于用户理解 │
│ │
│ 3. 权限撤销 │
│ ├─ 撤销组权限影响所有 │
│ ├─ 统一处理 │
│ └─ 便于权限状态同步 │
│ │
└─────────────────────────────────────────────────────────────┘5. 权限拒绝处理
5.1 权限拒绝类型
权限拒绝类型:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 普通拒绝 │
│ ├─ 用户选择"不允许" │
│ ├─ 可以再次请求 │
│ ├─ 显示说明对话框 │
│ └─ 建议解释权限用途 │
│ │
│ 永久拒绝 │
│ ├─ 用户选择"不允许"并勾选"不再询问" │
│ ├─ 无法再次自动请求 │
│ ├─ 需跳转到设置页面 │
│ └─ 用户手动开启权限 │
│ │
│ 系统拒绝 │
│ ├─ 系统限制(如儿童模式) │
│ ├─ 企业管理器限制 │
│ ├─ 安全软件拦截 │
│ └─ 需要解除限制 │
│ │
└─────────────────────────────────────────────────────────────┘5.2 权限拒绝处理策略
处理权限拒绝:
java
// 处理权限拒绝
public class PermissionHandler {
// 检查是否需要说明
public boolean shouldShowRationale(Activity activity, String permission) {
return ActivityCompat.shouldShowRequestPermissionRationale(
activity, permission);
}
// 检查是否永久拒绝
public boolean isPermissionPermanentlyDenied(Activity activity, String permission) {
return !ActivityCompat.shouldShowRequestPermissionRationale(
activity, permission);
}
// 跳转到设置页面
public void openAppSettings(Activity activity) {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", activity.getPackageName(), null);
intent.setData(uri);
activity.startActivity(intent);
}
// 显示权限说明对话框
public void showPermissionRationaleDialog(DialogFragment dialog) {
dialog.show(getSupportFragmentManager(), "PermissionRationale");
}
// 处理权限结果
public void handlePermissionResult(int requestCode, int[] grantResults) {
if (grantResults.length > 0) {
for (int result : grantResults) {
if (result == PackageManager.PERMISSION_GRANTED) {
// 权限已授予
usePermission();
} else {
// 权限被拒绝
handlePermissionDenied();
}
}
}
}
}6. 特殊权限
6.1 悬浮窗权限
SYSTEM_ALERT_WINDOW 权限:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 用途 │
│ ├─ 显示在其他应用之上 │
│ ├─ 创建悬浮窗 │
│ ├─ 画屏取词 │
│ └─ 通知提醒 │
│ │
│ 请求方式 │
│ ├─ 不能通过代码请求 │
│ ├─ 需跳转到设置页面 │
│ ├─ 用户手动开启 │
│ └─ Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION) │
│ │
│ 检查权限 │
│ ├─ Settings.canDrawOverlays() │
│ └─ 返回 boolean 值 │
│ │
└─────────────────────────────────────────────────────────────┘请求悬浮窗权限:
java
// 请求悬浮窗权限
public void requestOverlayPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.canDrawOverlays(this)) {
// 跳转到设置页面
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivity(intent);
} else {
// 权限已授予
createOverlay();
}
}
}6.2 修改系统设置权限
WRITE_SETTINGS 权限:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 用途 │
│ ├─ 修改系统设置 │
│ ├─ 修改辅助功能设置 │
│ ├─ 修改显示设置 │
│ └─ 修改网络设置 │
│ │
│ 请求方式 │
│ ├─ 不能通过代码请求 │
│ ├─ 需跳转到设置页面 │
│ ├─ 用户手动开启 │
│ └─ Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS) │
│ │
│ 检查权限 │
│ ├─ Settings.System.canWrite() │
│ └─ 返回 boolean 值 │
│ │
└─────────────────────────────────────────────────────────────┘请求修改系统设置权限:
java
// 请求修改系统设置权限
public void requestWriteSettingsPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.System.canWrite(this)) {
// 跳转到设置页面
Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivity(intent);
} else {
// 权限已授予
modifySettings();
}
}
}6.3 无障碍服务权限
BIND_ACCESSIBILITY_SERVICE 权限:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 用途 │
│ ├─ 无障碍辅助 │
│ ├─ 自动点击 │
│ ├─ 屏幕读取 │
│ ├─ 模拟触摸 │
│ └─ 应用自动化 │
│ │
│ 请求方式 │
│ ├─ 需实现 AccessibilityService │
│ ├─ 在 AndroidManifest.xml 声明 │
│ ├─ 跳转到无障碍设置页面 │
│ ├─ 用户手动开启 │
│ └─ Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS) │
│ │
│ 检查权限 │
│ ├─ AccessibilityManager.isAccessibilityEnabled() │
│ └─ 返回 boolean 值 │
│ │
└─────────────────────────────────────────────────────────────┘7. 权限自动授予
7.1 权限自动授予机制
权限自动授予:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 自动授予条件 │
│ ├─ 应用来自可信来源 │
│ ├─ 应用是系统应用 │
│ ├─ 应用是 Google Play 应用 │
│ ├─ 权限是普通权限 │
│ └─ 用户之前授予过同类权限 │
│ │
│ 不自动授予情况 │
│ ├─ 危险权限需要明确请求 │
│ ├─ 特殊权限需要手动设置 │
│ ├─ 用户明确拒绝过 │
│ └─ 应用来自非可信来源 │
│ │
└─────────────────────────────────────────────────────────────┘7.2 优化权限请求
权限请求优化:
java
// 权限请求优化
public class PermissionOptimizer {
// 按需请求权限
public void requestPermissionOnDemand(String permission) {
// 1. 检查功能是否需要
if (isFeatureNeeded(permission)) {
// 2. 在用户使用功能时请求
requestPermissionAtFeatureUse(permission);
}
}
// 分步请求权限
public void requestPermissionsStepByStep() {
// 1. 先请求核心功能权限
requestCorePermissions();
// 2. 用户使用时再请求其他权限
requestAdditionalPermissionsOnUse();
}
// 解释权限用途
public void explainPermissionUsage(String permission) {
// 1. 显示说明对话框
showPermissionExplanation(permission);
// 2. 解释权限用途
explainWhyNeeded(permission);
// 3. 展示隐私政策
showPrivacyPolicy();
}
}8. 权限检查最佳实践
8.1 权限检查清单
权限检查最佳实践:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 1. 最小权限原则 │
│ ├─ 只申请必要的权限 │
│ ├─ 避免过度申请 │
│ └─ 按功能模块申请 │
│ │
│ 2. 合理时机请求 │
│ ├─ 在需要时请求 │
│ ├─ 避免启动时批量请求 │
│ ├─ 提供功能降级方案 │
│ └─ 给予用户选择空间 │
│ │
│ 3. 友好的权限说明 │
│ ├─ 清晰解释权限用途 │
│ ├─ 说明为什么需要 │
│ ├─ 展示隐私政策 │
│ └─ 提供拒绝后的替代方案 │
│ │
│ 4. 优雅的错误处理 │
│ ├─ 处理权限拒绝 │
│ ├─ 提供功能降级 │
│ ├─ 引导用户设置权限 │
│ └─ 避免频繁骚扰 │
│ │
│ 5. 权限状态同步 │
│ ├─ 监听权限变化 │
│ ├─ 更新 UI 状态 │
│ ├─ 同步权限数据 │
│ └─ 处理权限撤销 │
│ │
└─────────────────────────────────────────────────────────────┘9. Android 版本权限变化
9.1 主要版本变化
Android 版本权限变化:
┌─────────────────────────────────────────────────────────────┐
│ │
│ Android 6.0 (M) │
│ ├─ 引入运行时权限 │
│ ├─ 危险权限需要运行时请求 │
│ └─ 权限可动态撤销 │
│ │
│ Android 7.0 (N) │
│ ├─ 权限组优化 │
│ ├─ 后台权限限制开始 │
│ └─ 通知权限细化 │
│ │
│ Android 8.0 (O) │
│ ├─ 后台启动限制 │
│ ├─ 精确位置权限 │
│ └─ Doze 模式加强 │
│ │
│ Android 9.0 (Pie) │
│ ├─ 后台位置限制 │
│ ├─ 分区存储开始 │
│ └─ 权限使用限制 │
│ │
│ Android 10 (Q) │
│ ├─ 模糊位置权限 │
│ ├─ 分区存储正式推出 │
│ ├─ 后台权限限制加强 │
│ └─ 一次权限授权 │
│ │
│ Android 11 (R) │
│ ├─ 一次性权限 │
│ ├─ 通知权限分类 │
│ ├─ 权限使用情况显示 │
│ └─ 后台权限限制进一步 │
│ │
│ Android 12 (S) │
│ ├─ 通知权限强制请求 │
│ ├─ 屏幕录制权限 │
│ ├─ 麦克风/相机指示器 │
│ └─ 权限请求优化 │
│ │
│ Android 13 (T) │
│ ├─ 媒体权限拆分 │
│ ├─ 通知权限正式引入 │
│ ├─ 短信权限细化 │
│ ├─ 通话权限细化 │
│ └─ 近场通信权限 │
│ │
│ Android 14 (U) │
│ ├─ 权限使用追踪 │
│ ├─ 权限管理改进 │
│ ├─ 隐私权限增强 │
│ └─ 应用锁权限 │
│ │
└─────────────────────────────────────────────────────────────┘9.2 版本适配代码
版本适配权限:
java
// 版本适配权限
public class VersionAdaptedPermission {
// 请求存储权限
public void requestStoragePermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
// Android 11+ 使用分区存储
requestScopedStorage();
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Android 6.0-10 使用运行时权限
requestRuntimeStoragePermission();
} else {
// Android 5.1 及以下安装时授权
useStorage();
}
}
// 请求位置权限
public void requestLocationPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// Android 10+ 区分精确/模糊位置
requestApproximateLocationPermission();
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Android 6.0-9 运行时权限
requestRuntimeLocationPermission();
} else {
// Android 5.1 及以下安装时授权
useLocation();
}
}
// 请求通知权限
public void requestNotificationPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
// Android 13+ 需要 POST_NOTIFICATIONS 权限
requestPostNotificationsPermission();
} else {
// Android 12 及以下不需要
showNotification();
}
}
}10. 面试考点
10.1 基础考点
1. 权限级别分类?
答案:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 权限级别 │
│ ├─ 普通权限 (Normal) 安装时自动授予 │
│ ├─ 危险权限 (Dangerous) 运行时请求 │
│ ├─ 签名权限 (Signature) 系统签名应用 │
│ └─ 签名或系统权限 系统应用 │
│ │
└─────────────────────────────────────────────────────────────┘2. 运行时权限请求流程?
答案:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 运行时权限请求流程 │
│ ├─ 检查权限状态 │
│ ├─ 检查是否需要说明 │
│ ├─ 请求权限 │
│ ├─ 用户选择允许或拒绝 │
│ └─ 回调处理结果 │
│ │
└─────────────────────────────────────────────────────────────┘10.2 进阶考点
1. 特殊权限有哪些?
答案:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 特殊权限 │
│ ├─ SYSTEM_ALERT_WINDOW 悬浮窗 │
│ ├─ WRITE_SETTINGS 修改系统设置 │
│ ├─ BIND_ACCESSIBILITY_SERVICE 无障碍服务 │
│ ├─ BIND_VPN_SERVICE VPN 服务 │
│ ├─ BIND_WALLPAPER 壁纸服务 │
│ └─ POST_NOTIFICATIONS 发送通知 (Android 13+) │
│ │
└─────────────────────────────────────────────────────────────┘2. 权限拒绝如何处理?
答案:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 权限拒绝处理 │
│ ├─ 普通拒绝:可以再次请求 │
│ ├─ 永久拒绝:跳转到设置页面 │
│ ├─ 提供功能降级方案 │
│ ├─ 解释权限用途 │
│ └─ 避免频繁骚扰 │
│ │
└─────────────────────────────────────────────────────────────┘10.3 高级考点
1. Android 13+ 新增权限?
答案:
┌─────────────────────────────────────────────────────────────┐
│ │
│ Android 13+ 新增权限 │
│ ├─ POST_NOTIFICATIONS 发送通知 │
│ ├─ READ_MEDIA_IMAGES 读取图片 │
│ ├─ READ_MEDIA_VIDEO 读取视频 │
│ ├─ READ_MEDIA_AUDIO 读取音频 │
│ ├─ READ_CALL_LOG 读取通话记录 │
│ ├─ READ_SMS 读取短信 │
│ └─ SEND_SMS/RECEIVE_SMS 发送/接收短信 │
│ │
└─────────────────────────────────────────────────────────────┘2. 权限请求最佳实践?
答案:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 权限请求最佳实践 │
│ ├─ 最小权限原则 │
│ ├─ 合理时机请求 │
│ ├─ 友好的权限说明 │
│ ├─ 优雅的错误处理 │
│ └─ 权限状态同步 │
│ │
└─────────────────────────────────────────────────────────────┘文档信息:
- 字数:约 14000 字
- 包含:权限级别、运行时权限、权限组、权限拒绝处理、特殊权限、权限自动授予、最佳实践、Android 版本变化、面试考点
- 代码示例:包含完整的权限请求、检查、处理代码
- ASCII 流程图:包含多个流程时序图和架构图
- 最佳实践:权限请求最佳实践、版本适配建议
建议:
- 掌握运行时权限请求的完整流程
- 了解各 Android 版本的权限变化
- 熟悉特殊权限的请求方式
- 学习权限请求的最佳实践