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 权限名称遵循 反向域名 + 权限路径 的命名规范,分为三类:
- 系统定义权限:前缀为
android.permission.,由平台提供,如android.permission.READ_CONTACTS。 - 第三方定义权限:使用开发者自己的包名作为前缀,如
com.example.myapp.permission.MY_CUSTOM。 - 特殊保护级别权限:如
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> 标签在链路中的初始作用。
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()前置校验,防止权限降级攻击。