2. 清单权限声明规范:uses-permission标签、权限名称格式、版本兼容性声明

2.1 权限声明的核心入口:<uses-permission> 标签

Android 系统要求所有运行时权限必须在 AndroidManifest.xml 文件中显式声明,这是权限请求链路的第一道关口。<uses-permission> 标签作为 <manifest> 的直接子元素,告知系统应用需要访问的受保护资源。缺少该声明,即使运行时发出 requestPermissions() 调用,系统也会直接拒绝授权。

标准声明语法:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.app">

    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    <application ...>
        ...
    </application>

</manifest>

标签属性说明:

属性 必填 说明 示例值
android:name 是 权限的唯一标识符字符串 "android.permission.CAMERA"
android:maxSdkVersion 否 该权限声明生效的最高 API Level,超过此级别后声明失效 "32"
android:requiredFeature 否 声明该权限依赖的设备硬件特性(API 33+) "android.hardware.camera"

高频错误陷阱:将 <uses-permission> 放置于 <application> 标签内部。该标签必须是 <manifest> 的直接子元素,放在 <application> 之前或与其并列,否则编译时不会报错,但运行时系统将忽略该声明,导致权限请求无响应。

2.2 权限名称格式:层次化命名空间与自定义权限

Android 权限名称遵循 反向域名 + 权限路径 的命名规范,分为三类:

  1. 系统定义权限:前缀为 android.permission.,由平台提供,如 android.permission.READ_CONTACTS。
  2. 第三方定义权限:使用开发者自己的包名作为前缀,如 com.example.myapp.permission.MY_CUSTOM。
  3. 特殊保护级别权限:如 android.permission.SYSTEM_ALERT_WINDOW,需通过 Settings 页面手动开启。
权限类别 命名格式 示例 常见所属组
Normal(普通) 系统前缀 + 描述性名称 android.permission.INTERNET 无运行时请求(自动授予)
Dangerous(危险) 系统前缀 + 资源名称 android.permission.CAMERA 相机、位置、存储等
Signature(签名) 系统或自定义前缀 android.permission.BIND_NOTIFICATION_LISTENER_SERVICE 仅同签名应用可申请
Custom(自定义) ${packageName}.permission.${NAME} com.example.app.permission.READ_DATA 应用间 IPC 保护

最佳实践:自定义权限的名称应使用全小写字母,以避免大小写敏感问题。同时,声明自定义权限时建议同时定义 android:protectionLevel,推荐使用 "signature" 或 "dangerous",以确保安全性和兼容性。

自定义权限声明示例:

<permission
    android:name="com.example.app.permission.ACCESS_SECURE_DATA"
    android:label="@string/perm_label_access_secure"
    android:description="@string/perm_desc_access_secure"
    android:protectionLevel="signature" />

2.3 版本兼容性声明:maxSdkVersion 与分级适配策略

从 Android 6.0(API 23)引入运行时权限模型后,权限声明需要关注版本兼容性问题。核心机制是 android:maxSdkVersion 属性,用于控制声明在特定 API 级别后失效。

  • 作用场景:当某个权限在更高版本的 Android 中被废弃、合并或自动授予时,通过 maxSdkVersion 移除旧声明避免不必要的运行时请求。
  • 典型用例:Android 10(API 29)引入 ACCESS_BACKGROUND_LOCATION 后,旧版 ACCESS_FINE_LOCATION 声明需要配合 maxSdkVersion 进行剥离。

版本兼容声明模板:

<!-- 在 API 28 及以下需单独声明写存储权限 -->
<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="28" />

<!-- API 29+ 使用分区存储,旧权限自动失效 -->
<!-- 无需再声明 WRITE_EXTERNAL_STORAGE -->

2.4 权限声明链路图:从 Manifest 到运行时授权

下面这张内联 SVG 流程图展示了权限声明从 AndroidManifest.xml 解析到最终运行时授权的完整路径,突出了 <uses-permission> 标签在链路中的初始作用。

权限声明链路:Manifest → 系统服务 → 运行时授权 AndroidManifest.xml <uses-permission> 声明 PackageManagerService 解析 Manifest 权限列表 权限分组 & 保护级别 Normal / Dangerous / Signature 版本兼容判断 maxSdkVersion / SDK 版本过滤 运行时授权提示 Dialog / API 回调 授权结果回写 onRequestPermissionsResult() 关键链路说明 Manifest 声明 → PMS 缓存 → 版本兼容过滤 → 运行时权限申请 → 系统授权弹窗 → 开发者回调处理

2.5 常见错误与调试方法

  • 错误 #1:权限声明拼写错误
    现象:Logcat 输出 Permission Denial: not allowed to send broadcast 等异常。
    调试:使用 adb shell dumpsys package <package_name> 查看 requested permissions 列表,检查声明与实际权限名称是否一致。
  • 错误 #2:遗漏 maxSdkVersion 导致冗余请求
    现象:在 API 29+ 应用仍然请求 WRITE_EXTERNAL_STORAGE。
    修复:为旧权限添加 android:maxSdkVersion="28"。
  • 错误 #3:自定义权限未定义 protectionLevel
    现象:跨应用调用时权限校验失败或崩溃。
    修复:在 <permission> 声明中显式指定 android:protectionLevel="signature" 或 "dangerous"。

调试命令速查:

# 列出应用的已声明权限(包括未授予的)
adb shell dumpsys package com.example.app | grep -A 50 "Requested Permissions"

# 检查特定权限的元数据
adb shell dumpsys package permissions | grep -A 10 "android.permission.CAMERA"

2.6 性能与稳定性优化建议

  • 最小声明原则:只声明应用实际使用的权限,避免声明未使用的权限导致用户信任度降低和审核风险。
  • 版本分叉:使用 maxSdkVersion 对每个 API 级别精准控制权限集合,减少运行时不必要的弹窗。
  • 自定义权限检查:在 IPC 或 ContentProvider 中通过 checkCallingPermission() 或 ContextCompat.checkSelfPermission() 前置校验,防止权限降级攻击。