4. 权限声明与配置:module.json5权限声明、权限组概念、权限冲突解决策略

好,咱们进入第四个章节。权限声明这块,说实话,是很多鸿蒙开发者容易踩坑的地方。我自己刚接触那会儿,也闹过笑话——明明功能都写好了,一运行就崩,结果发现是权限没配。嗯,今天咱们就把这事儿彻底聊透。

4.1 module.json5 里的权限声明

在鸿蒙应用里,权限声明主要写在 module.json5 这个文件里。你想想看,它就像是你进小区时给保安看的通行证——哪些门能进,哪些房间不能进,都得写清楚。

具体怎么写呢?看这个例子:

{
  "module": {
    "name": "entry",
    "type": "entry",
    "requestPermissions": [
      {
        "name": "ohos.permission.CAMERA",
        "reason": "用于扫码识别功能",
        "usedScene": {
          "abilities": ["MainAbility"],
          "when": "inuse"
        }
      },
      {
        "name": "ohos.permission.READ_CONTACTS",
        "reason": "用于读取联系人信息",
        "usedScene": {
          "abilities": ["MainAbility", "SecondAbility"],
          "when": "always"
        }
      }
    ]
  }
}

这里有几个关键字段,我跟你掰扯一下:

  • name:权限名称,必须跟系统定义的一致,大小写敏感。我见过有人把 CAMERA 写成 Camera,结果折腾了半天。
  • reason:申请权限的原因。这个在应用商店审核时会看,别瞎写。我曾经见过有人写「为了好玩」,结果直接被拒。
  • usedScene:使用场景。包括哪些 Ability 在用,以及使用时机(inuse 表示前台使用,always 表示后台也能用)。
注意:敏感权限(比如位置、相机、麦克风)必须填写 reason 字段,否则编译会报错。这是鸿蒙的硬性要求,没得商量。

4.2 权限组的概念

权限组,说白了就是把相关的权限打包在一起。为什么要这么设计?你想想看,如果一个应用要拍照、录像、扫码,难道要用户点三次同意吗?那体验也太差了。

鸿蒙把权限分成了几个组,我列个表给你看:

权限组名称 包含的权限 典型场景
CAMERA ohos.permission.CAMERA 拍照、扫码、视频通话
LOCATION ohos.permission.LOCATION
ohos.permission.LOCATION_IN_BACKGROUND
地图导航、位置分享
MICROPHONE ohos.permission.MICROPHONE 语音输入、录音
STORAGE ohos.permission.READ_MEDIA
ohos.permission.WRITE_MEDIA
读取相册、保存文件

这里有个细节要注意:用户授权是按组授权的。比如用户同意了 CAMERA 组,那组里的所有权限就都开了。但反过来,如果你只申请了组里的一个权限,用户同意了,那其他权限并不会自动获得——嗯,这个逻辑有点绕,但很重要。

我的建议:尽量按组申请权限,别零散地申请。这样用户体验好,审核也容易过。我在项目里就吃过亏,一开始只申请了 READ_MEDIA,后来要写文件时又弹窗,用户直接给了差评。

4.3 权限冲突解决策略

权限冲突,说白了就是两个模块或者两个库都要用同一个权限,但申请方式不一样。这种情况在大型项目里特别常见。

我遇到过这么个事儿:项目里用了第三方图片库,它自己申请了 CAMERA 权限,但我们的主模块也申请了。结果运行时弹了两次授权框,用户直接懵了。

怎么解决?我总结了几个策略:

  1. 统一声明:所有权限都在主模块的 module.json5 里声明,第三方库的权限声明全部去掉。这是最干净的做法。
  2. 权限合并:如果第三方库的权限声明去不掉(比如是闭源的),那就用 overlay 机制覆盖掉。具体做法是在自己的配置里加上相同的权限,系统会自动合并。
  3. 运行时检查:在调用第三方库之前,先检查权限是否已经授予。如果没授予,先申请再调用。代码大概长这样:
// 检查权限
let result = await abilityAccessCtrl.createAtManager()
  .checkAccessTokenSync(
    bundleName,
    ohos.permission.CAMERA
  );

if (result !== 0) {
  // 申请权限
  await abilityAccessCtrl.createAtManager()
    .requestPermissionsFromUser(
      context,
      ["ohos.permission.CAMERA"]
    );
}

// 再调用第三方库
thirdPartyLib.startCamera();
核心原则:权限申请要「一次申请,多处复用」。别每个模块都去弹窗,用户会疯的。

4.4 避坑指南

最后,我把自己踩过的坑总结一下,你直接拿去用:

  • 别重复申请:同一个权限在多个 Ability 里申请,系统会合并,但弹窗次数不会减少。我建议只在入口 Ability 里统一申请。
  • 注意权限等级:鸿蒙的权限分 normal、system_basic、system_core 三级。普通应用只能申请 normal 级别的权限,别想着越级。
  • 动态权限要处理拒绝:用户点了「拒绝」后,别一直弹窗。我见过有的应用死循环弹窗,直接被系统拉黑了。
  • 测试时用真实设备:模拟器上权限申请可能不弹窗,真机上才会触发。我曾经在模拟器上测得好好的,一上真机就崩,尴尬死了。

嗯,权限这块内容其实挺多的,但核心就是这几点。你只要把 module.json5 写对,权限组概念搞清楚,冲突策略用上,基本就不会出大问题。下一章咱们聊聊权限的动态申请和用户交互,那个更有意思。