4. Android权限体系演进:从Android 6.0运行时权限到Android 13细粒度权限

聊到Android隐私合规,权限体系是绕不开的核心话题。我做了这么多年Android开发,亲眼看着权限管理从「粗放式」一步步走到今天的「精细化」。说实话,早期那会儿的权限设计,放到现在合规环境下,简直是个灾难。

这一节,我就带你捋一遍Android权限体系的演进脉络。你理解了为什么这么变,自然就知道该怎么适配了。

4.1 为什么权限体系必须演进?

先问个问题:你装一个手电筒App,它为什么要读取你的联系人?

嗯,这个问题在Android 6.0之前,没人管。开发者只要在AndroidManifest.xml里声明了权限,用户安装时点一下「同意」,App就能为所欲为。我当年接手过一个项目,里面有个功能只是给图片加个滤镜,结果权限声明里赫然写着READ_CONTACTS。问产品经理,他说「先申着,万一以后要用呢」——你看,这就是典型的野蛮生长。

从GDPR到国内的《个人信息保护法》,法律层面要求App只能收集「最小必要」的数据。所以Google不得不从系统层面把权限管起来。说白了,权限体系的每一次升级,都是在跟「滥用权限」这件事较劲。

4.2 关键里程碑:从6.0到13

我整理了一张演进图,你先有个整体印象:

6.0 10 11 13 运行时权限 安装时不再一次性授权 使用时动态弹窗询问 用户可随时撤销 分区存储 限制访问公共目录 引入媒体存储权限 不再直接读文件路径 单次权限 新增「仅本次允许」 权限自动重置 后台位置受限 细粒度媒体权限 图片/视频/音频分离 附近设备权限 通知权限独立 Android权限体系演进时间线

4.3 Android 6.0:运行时权限——划时代的变革

Android 6.0(API 23)引入了运行时权限模型。这是整个权限体系的分水岭。

核心变化:

  • 危险权限(Dangerous Permissions)需要在运行时动态申请
  • 用户可以在「设置」中随时撤销已授予的权限
  • App必须检查权限状态,处理被拒绝的情况

我记得当时适配6.0的时候,整个团队都懵了。以前写代码哪管什么权限检查,直接调API就行。现在倒好,每个敏感操作前面都得加一段权限判断逻辑。

关键概念:权限分组

Google把危险权限分成了若干组(Permission Group)。同一组内的权限,只要申请其中一个并得到授权,整组权限就自动获得了。举个例子:你申请了CAMERA,系统会自动把同组的RECORD_AUDIO也带上——虽然你根本没要。

这一点在合规审计时经常被忽略。我见过不少App,明明只用了拍照功能,却因为权限分组机制,悄悄拿到了录音权限。审计报告里直接标红。

4.4 权限分组与敏感权限列表

说到权限分组,我直接给你看官方定义的几大危险权限组:

权限组 包含的权限 典型场景
CALENDAR READ_CALENDAR, WRITE_CALENDAR 日程管理App
CAMERA CAMERA 拍照、扫码
CONTACTS READ_CONTACTS, WRITE_CONTACTS, GET_ACCOUNTS 通讯录同步
LOCATION ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION, ACCESS_BACKGROUND_LOCATION 地图、导航
PHONE READ_PHONE_STATE, CALL_PHONE, READ_CALL_LOG, WRITE_CALL_LOG, ADD_VOICEMAIL, USE_SIP, PROCESS_OUTGOING_CALLS 通话相关
STORAGE READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE 文件读写

⚠️ 避坑指南:我曾经在一个社交App项目里,因为用了READ_PHONE_STATE来获取设备标识,结果被第三方检测工具判定为「过度收集隐私」。后来才发现,这个权限属于PHONE组,一旦申请,整组权限都会被用户看到——哪怕你只用了IMEI。合规做法是改用AdvertisingId或者InstanceId,彻底绕开电话权限。

4.5 Android 10-11:分区存储与单次权限

Android 10(API 29)引入了分区存储(Scoped Storage)。说白了,就是不让App随便翻用户的相册和文件了。每个App只能在自己的沙盒目录里自由读写,访问公共目录必须走MediaStore。

到了Android 11(API 30),又加了单次权限(One-time Permission)。用户可以选择「仅本次允许」,App退出后权限自动回收。这个设计太聪明了——你想想看,用户扫描一个二维码,授权一次就够了,凭什么让App一直拿着相机权限?

💡 我的建议:适配Android 11时,一定要处理好权限被自动重置的情况。我见过一个App,用户授权了位置权限,用了一次后权限被重置,下次打开直接崩溃——因为没做权限状态检查。正确的做法是:每次使用敏感权限前,都调用checkSelfPermission()重新确认。

4.6 Android 13:细粒度权限——精准到每一张照片

Android 13(API 33)把权限细化到了极致。以前你申请READ_EXTERNAL_STORAGE,就能读到所有媒体文件。现在不行了,Google把媒体权限拆成了三个:

  • READ_MEDIA_IMAGES —— 只读图片
  • READ_MEDIA_VIDEO —— 只读视频
  • READ_MEDIA_AUDIO —— 只读音频

另外,BLUETOOTH相关的权限也变了。以前要申请ACCESS_FINE_LOCATION才能做蓝牙扫描,现在Android 13引入了BLUETOOTH_SCANBLUETOOTH_ADVERTISEBLUETOOTH_CONNECT三个独立权限,不再跟位置权限强绑定。

还有一个容易被忽略的点:通知权限。Android 13把通知也纳入了运行时权限体系。App必须申请POST_NOTIFICATIONS才能推送通知。我去年帮一个客户做合规改造,他们的App一升级到Android 13,通知全没了——就是因为没适配这个新权限。

4.7 权限申请最佳实践

讲了这么多历史,最后落到实操上。我总结了一套权限申请的「黄金流程」,你在项目里可以直接套用:

  1. 提前说明用途:不要一上来就弹系统对话框。先弹一个自定义的说明弹窗,告诉用户「我们需要访问相册来选择头像」,用户理解了,同意的概率高很多。
  2. 最小化申请:只申请当前功能必需的权限。别学我当年那个产品经理,把权限当储备粮。
  3. 处理拒绝:用户拒绝后,不要反复弹窗。如果用户勾选了「不再询问」,引导他去设置里手动开启。
  4. 检查权限状态:每次使用前都检查,不要缓存授权结果。用户随时可能去设置里关掉权限。
  5. 适配新版本:targetSdkVersion 及时更新到33+,用新的细粒度权限替代旧的。

代码示例(Kotlin):

// 检查并申请单张图片访问权限
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
    // Android 13+ 使用细粒度权限
    if (ContextCompat.checkSelfPermission(this, 
            Manifest.permission.READ_MEDIA_IMAGES) 
            != PackageManager.PERMISSION_GRANTED) {
        // 先解释为什么需要这个权限
        showPermissionRationale("选择头像需要访问您的相册")
        // 再发起申请
        ActivityCompat.requestPermissions(this,
            arrayOf(Manifest.permission.READ_MEDIA_IMAGES),
            REQUEST_CODE_PICK_IMAGE)
    }
} else {
    // 低版本使用传统存储权限
    // ... 兼容处理
}

核心原则:权限申请不是技术问题,是用户体验问题。你让用户感到被尊重,他就愿意授权。你硬来,他就去投诉你——到时候GDPR的罚单可不是闹着玩的。

好了,这一节的内容就到这里。权限体系这块水很深,但只要你理解了演进背后的逻辑——从「能用就行」到「最小必要」——适配起来就不会跑偏。


公众号:蓝海资料掘金营,微信deep3321