权限基础:Android权限模型、权限分类、运行时权限
Android 权限模型——我踩过的第一个坑
刚入行做 Android 开发那会儿,我犯过一个特别经典的错误:直接在 AndroidManifest 里写了一堆权限,然后以为用户安装时就自动授权了。结果在 Android 6.0 的设备上,app 一启动就崩溃——因为压根没处理运行时权限的弹窗。
说白了,Android 的权限模型就是一套「资源访问控制」机制。应用想调用摄像头、读取通讯录、获取位置,都得先经过系统这道门槛。系统会根据权限的级别,决定是自动放行、弹窗询问,还是直接拒绝。
我个人习惯把这个模型分为三个维度:声明端(AndroidManifest)、授予端(系统/用户)、检查端(代码里 checkPermission)。合规扫描里,我们重点看的就是声明与授予之间有没有偏差。
权限分类:普通、危险、签名——它们区别在哪?
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 只写了申请代码,却没处理用户拒绝后的降级路径——这就是合规风险。
shouldShowRequestPermissionRationale() 提前判断并引导用户去设置里手动打开。
一张图看懂 Android 权限体系
合规扫描怎么落地?给你一个小清单
讲完理论,我直接上实操的检查要点。你自己写代码时也可以按这个列表做自查:
- 清单文件里声明的所有权限,要跟代码里实际调用的 API 一一对应。我曾经接手过一个项目,AndroidManifest 里写了 CAMERA 权限,但业务代码里根本没用过摄像头——这就是违规。
- 危险权限必须动态申请,并且要在用户拒绝后给出合理降级。比如定位权限被拒,地图功能就应该隐藏或者提示「无法获取位置」。
- 注意权限组:同一个组内权限只需申请一次。不要重复弹窗惹用户烦。
- Android 11 及以上的权限自动重置:如果用户超过几个月没打开你的 app,系统会把之前授予的敏感权限收回来。你的 app 需要做好重新申请的准备。
嗯,基础部分就先到这。这些概念是后面所有扫描规则的核心,搞懂了它们,你才能理解为什么某个 SDK 会被标记「违规」。