4. 清单文件权限声明
Android 的权限体系并不是“代码里申请一下就生效”,它真正的入口在 AndroidManifest.xml。清单文件承担三类关键职责:声明应用需要使用的系统权限、声明应用依赖的硬件或软件能力、以及定义应用自身对外暴露的权限。理解这三类标签的语义差异,是后续运行时权限申请、权限拒绝处理、系统服务调用的基础。
一、核心原理:三类标签各司其职
清单中的权限相关标签可以按“方向”和“对象”来区分。<uses-permission> 表示“我要向系统借用能力”,是应用向系统提出的请求;<uses-feature> 表示“我依赖某种设备能力”,用于应用市场过滤和兼容性判断;<permission> 表示“我定义一项权限”,是应用对外发布的访问控制声明。三者方向不同,混用会导致权限申请失败或安装被过滤。
- uses-permission:请求系统已定义的权限,如
CAMERA、ACCESS_FINE_LOCATION。 - uses-feature:声明硬件/软件特性依赖,如
android.hardware.camera、android.hardware.bluetooth。 - permission:自定义权限,供本应用或其他应用申请,常用于组件暴露保护。
uses-permission 是“我要用别人的权限”,permission 是“我给别人定权限”。前者影响运行时授权,后者影响组件访问控制。
二、方法流程:从声明到生效
一个权限从清单声明到真正可用,通常经历“声明—安装登记—运行时申请—授权回调”四个阶段。对于普通权限,安装时即授予;对于危险权限,必须在运行时动态申请。清单声明是前提,缺少声明时运行时申请会直接失败。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.permissiondemo">
<!-- 请求系统权限 -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<!-- 声明硬件依赖 -->
<uses-feature
android:name="android.hardware.camera"
android:required="true" />
<uses-feature
android:name="android.hardware.bluetooth"
android:required="false" />
<!-- 自定义权限 -->
<permission
android:name="com.example.permissiondemo.permission.READ_DATA"
android:protectionLevel="signature" />
<application ... >
<activity
android:name=".ExportActivity"
android:exported="true"
android:permission="com.example.permissiondemo.permission.READ_DATA" />
</application>
</manifest>
上述配置中,CAMERA 和定位属于危险权限,需要在代码中调用 requestPermissions() 或 Activity Result API 申请;INTERNET 属于普通权限,安装即授予。自定义权限 READ_DATA 使用 signature 保护级别,只有相同签名的应用才能申请,从而保护 ExportActivity 不被任意第三方调用。
三、自定义权限与命名规范
自定义权限的命名必须全局唯一,推荐采用“应用包名 + .permission. + 权限名”的格式,例如 com.example.permissiondemo.permission.READ_DATA。这样既能避免与其他应用冲突,也便于在日志和代码审查中快速定位归属。保护级别 protectionLevel 决定了谁可以申请该权限,常见取值如下表。
| 保护级别 | 含义 | 典型场景 |
|---|---|---|
| normal | 低风险,安装即授予 | 应用内部数据读取 |
| dangerous | 高风险,需运行时申请 | 相机、定位、通讯录 |
| signature | 仅相同签名应用可申请 | 同一开发者多应用共享 |
| signatureOrSystem | 相同签名或系统应用 | 系统级组件保护 |
命名时还应避免使用 android.permission. 前缀,否则可能被系统误判或与系统权限冲突。权限名建议使用大写字母加下划线,保持与系统权限风格一致,便于识别。
四、案例场景:相机与定位的完整声明
假设要开发一个拍照并记录位置的应用。清单中需要同时声明相机权限、定位权限和相机硬件特性。若 uses-feature 中 android:required="true",则没有相机的设备在应用市场将无法安装;若设为 false,则允许安装但需在代码中判断硬件可用性。
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
运行时申请时,应检查 checkSelfPermission() 返回值,再决定是否弹出授权对话框。用户拒绝后,还应处理“不再询问”状态,引导用户前往设置页手动开启。
五、误区与复盘
uses-permission 就以为权限已生效。危险权限必须运行时申请,否则调用系统服务会抛出 SecurityException。常见误区二:把
uses-feature 当成权限声明。它不控制权限,只影响设备兼容性和市场过滤。常见误区三:自定义权限命名随意,导致与其他应用冲突或无法被正确识别。
复盘时建议检查三点:清单中是否声明了所有实际使用的权限;危险权限是否配套运行时申请逻辑;自定义权限的保护级别是否与组件敏感度匹配。只有清单声明、运行时申请、组件保护三者一致,权限体系才真正闭环。
掌握清单权限声明后,下一步才是运行时申请与拒绝处理。清单是“契约”,运行时是“执行”,两者缺一不可。建议在每次新增系统服务调用前,先回到清单确认权限声明是否完整,再编写申请逻辑,避免出现“代码写了、权限没声明”的低级错误。