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 当成权限声明。它不控制权限,只影响设备兼容性和市场过滤。
常见误区三:自定义权限命名随意,导致与其他应用冲突或无法被正确识别。

复盘时建议检查三点:清单中是否声明了所有实际使用的权限;危险权限是否配套运行时申请逻辑;自定义权限的保护级别是否与组件敏感度匹配。只有清单声明、运行时申请、组件保护三者一致,权限体系才真正闭环。

AndroidManifest.xml 权限声明结构 AndroidManifest.xml uses-permission 向系统请求权限 uses-feature 声明硬件/软件依赖 permission 自定义对外权限 示例 CAMERA ACCESS_FINE_LOCATION 示例 android.hardware.camera required=true/false 示例 com.example.permission.READ_DATA protectionLevel=signature 生效路径:清单声明 → 安装登记 → 运行时申请 → 授权回调 危险权限必须运行时申请,普通权限安装即授予

掌握清单权限声明后,下一步才是运行时申请与拒绝处理。清单是“契约”,运行时是“执行”,两者缺一不可。建议在每次新增系统服务调用前,先回到清单确认权限声明是否完整,再编写申请逻辑,避免出现“代码写了、权限没声明”的低级错误。