三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

Android 11权限适配:从‘单次授权’到‘软件包可见性’,一份给老项目的避坑清单

Android 11权限适配:从‘单次授权’到‘软件包可见性’,一份给老项目的避坑清单

Android 11权限适配:老项目升级的实战避坑指南

当你的老项目终于决定升级到Android 11时,那些陈旧的权限代码就像定时炸弹一样随时可能引爆。作为经历过多次系统升级的老兵,我深知这种升级过程中的痛苦——尤其是当你的targetSdkVersion还停留在石器时代的时候。本文将带你直击三个最致命的权限变更点,用实战经验帮你避开那些让应用崩溃或功能静默失效的深坑。

1. 单次授权:权限管理的全新游戏规则

Android 11彻底改变了位置、麦克风和摄像头权限的授权方式。还记得以前用户只能在"始终允许"和"拒绝"之间二选一吗?现在系统增加了一个"仅限这一次"的选项,这看似微小的变化却对应用逻辑产生了深远影响。

单次授权的核心行为特征

  • 临时访问窗口:当Activity可见时权限有效,转入后台后短时间内仍可访问(具体时长由系统决定)
  • 前台服务例外:如果在Activity可见时启动了相关前台服务,权限会持续到服务停止
  • 随时可撤销:用户可以在系统设置中随时撤回授权,此时应用进程会被终止
// 传统权限请求方式需要调整 if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { // 现在这里可能会得到单次授权 ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_CODE); }

实际案例:我们有一个健身应用,在升级后突然收到大量用户投诉说跑步轨迹记录不全。原因正是我们假设一旦获得位置权限就会持续有效,没有处理单次授权场景。修复方案是在onResume()中检查权限状态,并在必要时重新请求。

关键提示:所有依赖这些权限的功能都需要增加授权状态检查,不能假设一次授权永久有效

2. 后台位置权限:从简单到复杂的进化

Android 11将后台位置权限(ACCESS_BACKGROUND_LOCATION)完全独立出来,形成了更严格的管控机制。这直接影响了需要持续位置跟踪的应用,如运动追踪、家庭安防等场景。

新旧行为对比表

行为特征Android 10及以前Android 11
权限选项"始终允许"直接包含前后台必须分开请求
请求流程可与其他权限同时请求必须单独请求且先有前台权限
用户引导直接弹窗跳转系统设置页
// 正确的递增请求示例 fun requestLocationPermissions() { when { hasBackgroundLocationPermission() -> { // 已有全部权限 } hasForegroundLocationPermission() -> { // 只有前台权限,请求后台权限 requestPermissions(arrayOf(ACCESS_BACKGROUND_LOCATION), REQ_BACKGROUND) } else -> { // 先请求前台权限 requestPermissions(arrayOf(ACCESS_FINE_LOCATION), REQ_FOREGROUND) } } }

踩坑记录:我们最初尝试同时请求前后台权限,结果应用直接崩溃。日志显示系统抛出了SecurityException,这正是Android 11的新限制——禁止混合请求后台位置权限与其他权限。

3. 软件包可见性:那些突然"消失"的应用

这是最隐蔽也最危险的变化。在Android 11上,你的应用默认"看不见"其他应用了,这直接影响了分享、支付、跳转等核心功能,而且失败时没有任何提示!

受影响的典型场景

  • 检查微信是否安装以便启动分享
  • 查询支付宝包信息验证版本
  • 获取设备上所有浏览器列表
  • 通过包名判断特定应用是否存在
<!-- 解决方案:在AndroidManifest中添加queries声明 --> <queries> <!-- 明确声明需要交互的包名 --> <package android:name="com.tencent.mm" /> <!-- 微信 --> <package android:name="com.eg.android.AlipayGphone" /> <!-- 支付宝 --> <!-- 或者通过intent-filter声明 --> <intent> <action android:name="android.intent.action.SEND" /> <data android:mimeType="image/*" /> </intent> </queries>

血泪教训:我们的应用有一个"分享到微博"的功能,在测试时一切正常,因为测试机安装了开发工具。但上线后用户完全无法使用,日志显示queryIntentActivities返回空列表。根本原因就是缺少包可见性声明,而startActivity()调用却依然能工作,这种静默失败最危险。

4. 其他不容忽视的权限细节

除了上述三大变更,Android 11还引入了一些容易被忽略但同样重要的权限调整:

前台服务类型强化

<!-- 访问麦克风或摄像头的前台服务必须声明类型 --> <service android:name=".RecorderService" android:foregroundServiceType="microphone|camera" />

权限自动重置

// 检查用户是否开启了自动重置 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { boolean isWhitelisted = getPackageManager().isAutoRevokeWhitelisted(); if (!isWhitelisted) { // 引导用户前往设置页 Intent intent = new Intent(Settings.ACTION_AUTO_REVOKE_PERMISSIONS); intent.setData(Uri.fromParts("package", getPackageName(), null)); startActivity(intent); } }

电话权限细分

<!-- 获取电话号码需要新增专门权限 --> <uses-permission android:name="android.permission.READ_PHONE_NUMBERS" />

5. 实战检查清单

为了确保老项目平稳过渡,建议按照以下步骤系统性地检查权限相关代码:

  1. 全局搜索权限相关代码

    # 在项目目录下执行 grep -r "requestPermissions(" app/src/ grep -r "checkSelfPermission(" app/src/
  2. 重点检查三类敏感权限

    • 位置权限(ACCESS_FINE_LOCATION/ACCESS_COARSE_LOCATION)
    • 麦克风权限(RECORD_AUDIO)
    • 摄像头权限(CAMERA)
  3. 验证包可见性影响

    • 所有使用PackageManager.queryIntentActivities()的地方
    • 所有显式检查其他应用是否安装的逻辑
    • 分享、支付等跨应用交互功能
  4. 测试边界场景

    • 授予单次授权后转入后台
    • 拒绝两次权限请求后的表现
    • 长时间未使用应用的权限自动重置
  5. 兼容性测试工具

    # 启用特定变更进行测试 adb shell am compat enable FORCE_SCOPED_STORAGE com.your.package adb shell am compat enable FORCE_PACKAGE_VISIBILITY com.your.package

在经历了三个老项目的Android 11升级后,我发现最耗时的不是实现新功能,而是找出那些基于旧版系统假设的隐蔽代码。特别是那些没有崩溃但功能异常的情况,往往需要结合用户反馈和详细日志分析才能定位。建议在升级前先建立完善的监控体系,确保能及时发现生产环境中的权限相关问题。

← 返回列表