直答: 统计SDK本身只声明网络类normal权限,不会触发权限弹窗;弹窗来自定位、相机、通讯录等user_grant业务权限,与数据上报链路无关。
现象:群里那张弹窗截图,先别急着回滚SDK
前阵子有个测试同学在群里甩了张权限弹窗截图:App冷启动刚跑完,就弹了一屏"是否允许获取位置信息",跟着一句"是不是统计SDK搞的,要不先回滚"。群里七嘴八舌,产品担心把用户吓跑,前端已经准备去卸载统计依赖了。我当时没急着接话,先让他把工程发我------这类截图我见太多,十张里有九张,弹窗根本不是统计SDK弹的。
先把症状固定下来:是冷启动必弹,还是点某个功能才弹?弹窗标题写的是定位、相机还是存储?不同症状指向完全不同的根因。下面按排错诊断的顺序走:先列嫌疑清单,再分步排查,最后用一张诊断表把根因钉死。
可能原因:先列三张嫌疑清单
在动手翻代码之前,先把可能性收敛成三条,避免无目的乱查:
- 嫌疑一:统计SDK自己申请了敏感权限。 直觉上大家会先怀疑它,毕竟"接完才出现"。但按华为HarmonyOS权限模型,统计SDK需要的是网络类权限,理论上不应该弹窗。
- 嫌疑二:module.json5在拷贝模板时多写了权限。 示例工程里常带定位、相机等权限,拷进自己工程后没删,冷启动时跟着一起声明出来。
- 嫌疑三:弹窗来自业务模块或其他SDK。 弹窗是按App整体声明触发的,系统不区分这个权限是统计要的还是地图要的,最容易被误归到统计头上。
分步排查:四步走,每步都写清预期结果
排查不要跳步,跳步最容易冤枉SDK。下面四步按顺序来,每一步给出检查动作和预期;预期对不上,就停在这一步继续深挖,不要急着进下一步。
第1步:临时移除统计SDK做冷启动复现。 把统计SDK从工程里临时摘掉,跑一遍干净的冷启动。预期结果:如果弹窗消失,说明嫌疑一成立,问题在SDK侧;如果弹窗依旧,立刻排除统计SDK,转第2步去查配置。多数情况下这一步就能把统计"洗白"。
第2步:打开module.json5看requestPermissions数组。 华为设备开发文档要求,应用必须在module.json5的requestPermissions标签中声明权限,user_grant权限还必须带reason和usedScene两个字段。把这段配置完整翻出来,谁在申请敏感权限基本一目了然。
kotlin
// 示意:打开 module.json5,先看 requestPermissions 全貌
"requestPermissions": [
{ "name": "ohos.permission.INTERNET" },
{ "name": "ohos.permission.GET_NETWORK_INFO" },
{
"name": "ohos.permission.LOCATION",
"reason": "$string:location_reason",
"usedScene": { "abilities": ["EntryAbility"], "when": "inuse" }
}
]
判断分支很直接:上面INTERNET、GET_NETWORK_INFO两项没写reason和usedScene,它们是normal权限,系统自动授权、不弹窗;唯独LOCATION带了reason和usedScene,它才是冷启动弹窗的真凶。
第3步:按授权方式把权限分成两堆。 华为文档把授权方式分为system_grant和user_grant两类。system_grant类在安装后由系统自动授予,运行时不能也不需要弹窗;user_grant类涉及用户个人信息,必须运行时弹窗征得同意。把requestPermissions里每一项都归到这两堆之一,弹窗来源就锁定在user_grant那一堆里。
第4步:真机进系统设置核对实际授权列表。 装上包后进入系统设置的应用权限页,看App实际申请了哪些权限。预期:这个列表和module.json5的requestPermissions一致;如果一致但运行时没弹窗,说明那些权限都是normal或已被系统自动授予。就鸿蒙端的设备信息采集而言,网络类型一般来自GET_NETWORK_INFO,属于normal权限,不依赖定位,也不需要读手机标识符。
根因定位:用诊断表把弹窗钉到具体权限项
到这一步,把module.json5里所有带reason、usedScene的权限逐项填进下面这张诊断表。"预期"是它该有的行为,"实际"是你在真机上观察到的结果,"结论"一栏用来判定它是不是弹窗真凶。
| 检查项 | 预期 | 实际 | 结论 |
|---|---|---|---|
| ohos.permission.INTERNET | normal,自动授权,不弹窗 | 未弹窗,网络上报正常 | 与弹窗无关 |
| ohos.permission.GET_NETWORK_INFO | normal,自动授权,不弹窗 | 未弹窗,能识别网络类型 | 与弹窗无关 |
| ohos.permission.GET_WIFI_INFO | 可选normal,不弹窗 | 未声明或未弹窗 | 与弹窗无关 |
| ohos.permission.LOCATION | user_grant,运行时弹窗 | 冷启动即弹"获取位置信息" | 弹窗真凶,对应地图业务 |
| ohos.permission.CAMERA | user_grant,用到相机才该弹 | 未在相机场景却冷启动弹 | 冗余声明,需删或延后 |
| ohos.permission.READ_IMAGEVIDEO | user_grant,读相册才该弹 | 冷启动弹窗 | 模板拷贝带入,按业务核对 |
需要强调:弹窗按App整体声明触发,系统不会区分"这权限是统计要的还是业务要的",所以归因必须落到具体权限项上,而不是落到某个SDK身上。还有一个易被忽略的点:个别权限在不同API版本上的级别会变,排查时要对照当前targetSdkVersion对应的权限列表逐项核对,别凭记忆下结论。
举个我自己踩过的例子:某次升级targetSdkVersion之后,原本以为不弹窗的一项权限,在新系统版本上被归到了user_grant,冷启动突然开始弹窗。一开始怎么都查不到是统计引起的,后来对着新版权限列表一项项过,才发现是这一项级别变了。所以"凭上一版经验"在鸿蒙权限排查里是靠不住的,每次升API版本都要把requestPermissions重新对一遍官方权限列表。
修复方案:统计权限收敛最小集,业务权限按需申请
根因清楚后,修复分两步。第一步,把统计相关权限收敛到最小集合,只留INTERNET和GET_NETWORK_INFO,确实需要WiFi信息才加GET_WIFI_INFO。第二步,把业务权限改成"用到时再申请",而不是冷启动一把全弹------华为文档建议在用户即将用到对应功能时再发起授权,接受度更高。
json
// 修复后:module.json5 中统计相关最小权限声明
"requestPermissions": [
{ "name": "ohos.permission.INTERNET" },
{ "name": "ohos.permission.GET_NETWORK_INFO" }
// LOCATION 等业务权限移到地图页运行时按需申请
]
从数据侧看,业务权限被用户拒绝并不影响统计上报:只要INTERNET权限在,埋点数据就能通过HTTP发到打点地址。在统计控制台的实时概览里,可以看到冷启动后的事件是否正常到达,用它来验证"权限拒绝后上报链路有没有断"最直接。Web端埋点通常先写入一个全局队列变量、再由 SDK 取出上报,鸿蒙端则在SDK初始化后走同样的网络通道,二者都不依赖定位。建议把权限声明按模块拆分维护:统计的normal权限放基础模块,业务的user_grant权限放对应feature模块,code review时一眼能看出谁加的。
normal权限自动授权不弹窗,user_grant权限运行时弹窗征得用户同意
结尾:上线前最后问自己三个问题
第一个问题:这个权限真的需要吗?把requestPermissions里每一项对着功能问一遍,答不上来"它服务哪个页面"的,就是从模板里拷进来的冗余,删掉。统计相关只留INTERNET和GET_NETWORK_INFO两个normal权限,业务权限等到用户真要用那个功能时再申请,别在冷启动一把全弹。
第二个问题:reason写清楚了吗?凡是带reason和usedScene的user_grant权限,那句理由是不是说人话、能不能让用户一眼看懂你为什么要它。冷启动就弹一屏定位、理由却写得含糊,是劝退率最高的一种写法;该延后的权限延后到使用场景里再申请,接受度会好得多。
第三个问题:用户拒绝之后,上报链路断了吗?手动关掉某个业务权限再触发一次操作,在统计控制台的实时概览里看事件是否照常到达。只要INTERNET权限在,埋点就能走HTTP上报,业务权限被拒不该影响统计。三个问题都答得上来,再点上线。 
从冷启动复现到module.json5逐项核对的排查路径
数据来源:
- HarmonyOS设备开发文档《应用权限管控概述》(system_grant与user_grant权限分类)
- HarmonyOS设备开发文档《声明权限》
- HarmonyOS设备开发文档《module.json5配置文件》(requestPermissions字段定义)
- 456数据官网(HarmonyOS接入与权限说明)