权限基础:Android权限模型、权限分类、运行时权限

Android 权限模型——我踩过的第一个坑

刚入行做 Android 开发那会儿,我犯过一个特别经典的错误:直接在 AndroidManifest 里写了一堆权限,然后以为用户安装时就自动授权了。结果在 Android 6.0 的设备上,app 一启动就崩溃——因为压根没处理运行时权限的弹窗。

说白了,Android 的权限模型就是一套「资源访问控制」机制。应用想调用摄像头、读取通讯录、获取位置,都得先经过系统这道门槛。系统会根据权限的级别,决定是自动放行、弹窗询问,还是直接拒绝。

我个人习惯把这个模型分为三个维度:声明端(AndroidManifest)、授予端(系统/用户)、检查端(代码里 checkPermission)。合规扫描里,我们重点看的就是声明与授予之间有没有偏差。

核心记忆点:权限不是写进清单就万事大吉,运行时权限必须动态申请,否则在 targetSdk >= 23 的设备上直接闪退。

权限分类:普通、危险、签名——它们区别在哪?

Android 把权限分成三类,我刚开始总是混淆「普通」和「危险」的边界。直到有一次在项目里把 ACCESS_WIFI_STATE 当成了危险权限去动态申请,结果用户点拒绝后网络功能异常——其实它只需要在清单里声明就够了。

1. 普通权限(Normal Permissions)

这类权限影响小,系统在安装时自动授予应用。比如 INTERNET、ACCESS_NETWORK_STATE、BLUETOOTH 等。你只需要在 AndroidManifest 里声明,无需在代码里弹窗请求。

我自己的经验是:合规扫描里最容易忽略的就是普通权限的过度声明。有些 SDK 在清单里写了五六个普通权限,其实只用了其中两个。我们后面讲扫描策略时会重点说这个。

2. 危险权限(Dangerous Permissions)

涉及用户隐私的权限,比如定位、相机、录音、通讯录。系统必须弹窗让用户确认。注意:危险权限分成权限组,只要组内一个权限被授予,同组的其他权限会自动获得授权。

避坑指南:我曾经在项目里同时申请了 ACCESS_FINE_LOCATION 和 ACCESS_COARSE_LOCATION,结果弹了两次窗,用户体验极差。其实它们同属 LOCATION 组,只需申请一个,另一个就能用到。

// 不要这样写 —— 我当年犯过的错
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
    != PackageManager.PERMISSION_GRANTED) {
    requestPermissions(new String[]{
        Manifest.permission.ACCESS_FINE_LOCATION,
        Manifest.permission.ACCESS_COARSE_LOCATION
    }, REQUEST_CODE);
}

// 正确做法:只需申请精细定位
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
    != PackageManager.PERMISSION_GRANTED) {
    requestPermissions(new String[]{
        Manifest.permission.ACCESS_FINE_LOCATION
    }, REQUEST_CODE);
}
// 同组粗定位会自动跟随授权

3. 签名权限(Signature Permissions)

只有使用同一数字签名的应用才能获取。比如系统预置应用之间的通信。对第三方 SDK 合规扫描来说,遇到了也要标记,因为普通商业 app 几乎不会用到签名权限。

分类授予方式例子合规关注点
普通安装时自动授予INTERNET, NFC检查是否有冗余声明
危险运行时弹窗CAMERA, LOCATION是否动态申请,用户拒绝后是否降级
签名同签名自动授予BIND_ACCESSIBILITY_SERVICE一般三方SDK不允许使用

运行时权限——你永远猜不到用户会点「拒绝」

Android 6.0(API 23)引入了运行时权限模型。之前是「安装时一次性授权」,之后变成「使用时逐个弹窗」。这个改变被我们戏称为「开发者噩梦」——因为你不仅要写申请代码,还要处理用户点击「拒绝」甚至「不再询问」的情况。

我建议你记住三个关键版本演变:

  • Android 6.0:引入运行时权限,危险权限必须动态申请。
  • Android 10:新增权限限制,比如不允许后台启动 Activity 时访问位置。
  • Android 11:权限自动重置——如果用户长时间不用 app,系统会撤销之前授予的权限。

合规扫描时,我们需要确认 SDK 是否正确处理了 onRequestPermissionsResult 中的拒绝逻辑。很多 SDK 只写了申请代码,却没处理用户拒绝后的降级路径——这就是合规风险。

注意:Android 11 之后,如果用户两次拒绝权限申请并勾选「不再询问」,后续弹窗将不再出现。你必须用 shouldShowRequestPermissionRationale() 提前判断并引导用户去设置里手动打开。

一张图看懂 Android 权限体系

Android 权限体系结构图 Android 权限模型 普通权限 危险权限 签名权限 安装时自动授予 运行时动态申请 → 弹窗询问用户 同签名自动授予 用户操作 授权 / 拒绝 / 不再询问 授权成功:可使用功能 拒绝:降级功能 或 闪退 不再询问:引导去设置 合规扫描重点:检查危险权限是否动态申请、是否处理拒绝回调 以及是否有冗余普通权限声明

合规扫描怎么落地?给你一个小清单

讲完理论,我直接上实操的检查要点。你自己写代码时也可以按这个列表做自查:

  1. 清单文件里声明的所有权限,要跟代码里实际调用的 API 一一对应。我曾经接手过一个项目,AndroidManifest 里写了 CAMERA 权限,但业务代码里根本没用过摄像头——这就是违规。
  2. 危险权限必须动态申请,并且要在用户拒绝后给出合理降级。比如定位权限被拒,地图功能就应该隐藏或者提示「无法获取位置」。
  3. 注意权限组:同一个组内权限只需申请一次。不要重复弹窗惹用户烦。
  4. Android 11 及以上的权限自动重置:如果用户超过几个月没打开你的 app,系统会把之前授予的敏感权限收回来。你的 app 需要做好重新申请的准备。

嗯,基础部分就先到这。这些概念是后面所有扫描规则的核心,搞懂了它们,你才能理解为什么某个 SDK 会被标记「违规」。