权限配置:三大高频翻车现场
权限配置,是SDK接入的第一道坎。很多人觉得不就是加几行声明嘛,有什么难的?结果一上线,crash率直接飙升。
我自己最开始做SDK接入时,也在这块栽过跟头。Android权限改了又改,iOS隐私描述写得不清楚就被拒。说白了,权限配置的坑,是踩一个少一个。
4.1 Android权限声明:高频翻车现场
Android权限声明,最容易出问题的就三个点:清单文件声明遗漏、权限组变更、以及Android 11以后的「包可见性」变化。
4.1.1 清单文件声明遗漏
SDK文档里写了要哪些权限,但接入时漏了。这问题我见过无数次。比如定位SDK,文档要求ACCESS_FINE_LOCATION和ACCESS_COARSE_LOCATION,很多人只加了其中一个。
为什么会这样?因为大部分功能在低版本Android上跑得通,但到了Android 10+,精确位置和模糊位置的权限行为就分开了。只声明一个,某些系统会出现静默失败。
<!-- 正确做法:把SDK需要的权限全部列全 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
4.1.2 权限组变更的坑
Android权限分组,不同版本不一样。我记得有一次排查一个定位问题,用户反馈在Android 12上定位不准,但在Android 11上正常。查了半天,发现是BLUETOOTH_SCAN权限在Android 12被单独拆出来了。
嗯,这里要注意:从Android 12开始,蓝牙相关的权限从原来的位置权限组里独立了。如果你接入了蓝牙SDK,还要同时声明位置权限,得额外申请BLUETOOTH_CONNECT和BLUETOOTH_SCAN。
| Android版本 | 权限变化 | 影响范围 |
|---|---|---|
| Android 10 | 后台位置权限单独申请 | 定位SDK、地图SDK |
| Android 11 | 包可见性(QUERY_ALL_PACKAGES) | 支付SDK、分享SDK |
| Android 12 | 蓝牙权限拆分 | 蓝牙SDK、IoT SDK |
| Android 13 | 通知权限(POST_NOTIFICATIONS) | 推送SDK、IM SDK |
4.2 iOS隐私权限:被拒稿的最常见原因
iOS的隐私权限,一直是App Store审核的重点。SDK接入时,如果隐私权限描述写得不好,或者多加了不必要的权限,审核员直接打回。
4.2.1 Info.plist声明必须完整
iOS的权限声明,必须在Info.plist里加上对应的Key。比Android严格得多。Android漏了声明的权限最多功能不可用,iOS漏了声明的权限直接崩溃。
<!-- Info.plist 中的隐私权限声明示例 -->
<key>NSLocationWhenInUseUsageDescription</key>
<string>此App需要访问您的位置信息以提供地图导航服务</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>此App需要访问您的相册以保存截图</string>
<key>NSCameraUsageDescription</key>
<string>此App需要使用相机进行扫码识别</string>
4.2.2 权限描述信息的写法
我个人习惯在写权限描述时,坚持「功能驱动」原则。不要写模糊的描述。比如「我们需要你的位置信息」,审核员一看就觉得你动机不明。
更好的写法是:
- 明确说明:这个权限具体用于哪个功能
- 和SDK功能挂钩:不要出现和App功能无关的权限
- 不要夸大:明明只需要一次定位,却写「持续后台定位」
4.2.3 iOS 14+的权限透明化
iOS 14开始,苹果大大加强了用户隐私控制。比如相册权限,从「全部照片」变成了「选中的照片」。定位权限也从「精确位置」变成了「大致位置」。
SDK接入时,需要考虑这些变化。尤其那些依赖精确定位的SDK(比如导航SDK、签到SDK),如果用户只给了大致位置,功能就会受限。
4.3 动态权限申请:最容易被忽略的崩溃点
Android 6.0(API 23)之后,敏感权限需要运行时动态申请。iOS从iOS 10开始,所有隐私权限都需要弹窗询问。这块的常见问题,集中在申请时机、回调处理、以及多次拒绝后的处理。
4.3.1 申请时机不对
很多人喜欢在App启动时一股脑把权限全申请了。这其实不好。用户会觉得莫名其妙,而且一旦被拒绝,后面功能再申请就更难了。
我一般这样做:
- 按需申请:只有当用户真正要用到某个功能时,再弹出权限申请
- 先解释后申请:弹出一个简要说明页,告诉用户为什么需要这个权限
- 不要重复弹窗:用户拒绝后,短时间内不要再次弹窗
4.3.2 回调处理不全
动态权限申请的回调,是另一个容易出问题的地方。很多人只处理了「同意」的情况,没有处理「拒绝」和「不再询问」的情况。
我记得有一次排查线上crash,发现用户在拒绝定位权限后,SDK直接调用了定位方法,没有做权限检查,导致空指针异常。
// 错误的写法:没有做权限检查
LocationManager.requestLocationUpdates(...)
// 正确的做法:调用前检查权限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
LocationManager.requestLocationUpdates(...)
} else {
// 权限未授予,提示用户或降级处理
showPermissionDeniedDialog()
}
4.3.3 iOS的动态权限差异
iOS的权限申请和Android有很大不同。iOS只能申请一次,用户拒绝后系统不会再弹窗。你必须通过UIApplication.openSettingsURLString引导用户去设置。
所以接入iOS SDK时,一定要处理拒绝后的交互。不能像Android那样反复申请。
| 对比项 | Android | iOS |
|---|---|---|
| 申请次数 | 可多次申请(受系统限制) | 仅一次(之后需手动开启) |
| 拒绝后处理 | 引导至系统设置 | 引导至系统设置 |
| 权限分组 | 有(精准/粗略位置等) | 无(每个权限独立) |
| 权限描述 | 无强制要求 | Info.plist必须填写 |
| 兼容性要求 | 需适配各版本变化 | 需适配iOS 14+变化 |
4.4 权限配置的排查流程
当你发现SDK功能异常时,怎么定位是不是权限问题?我有一套固定的排查流程:
- 检查权限声明:Android看AndroidManifest.xml,iOS看Info.plist,确保所有权限都声明了
- 检查动态申请代码:确保在调用敏感功能前,已经申请了对应权限
- 检查权限状态:运行时打印所有相关权限的状态,看看是未申请、已拒绝、还是已授予
- 检查系统版本:不同版本的权限行为不同,优先确认目标机型版本
- 检查SDK版本:有些老版本的SDK没有适配新系统的权限变化
权限配置这块,说难也难,说简单也简单。关键是细心和耐心。每次接入新SDK,先把权限清单列清楚,再按流程检查一遍,很多问题都能提前发现。