升级后功能打不开,先别乱加权限:HarmonyOS 声明、授权与拒绝回退
升级后功能打不开,先别乱加权限:HarmonyOS 声明、授权与拒绝回退
灯牌应用准备增加“扫码配对”和“导入图片”时,开发者常把两个现象混为一谈:代码编译通过,功能却打不开,于是往 module.json5 里多填几个权限名。问题是,配置文件中的声明、系统运行时授权、实际调用的能力是三道关卡;多写权限既未必解决错误,还会扩大隐私说明和审核范围。

本文以 Flutter 鸿蒙宿主的权限盘点为起点,给出从功能到接口、从接口到权限、从权限到用户操作的核查方法。参考工程 ohos/entry/src/main/module.json5 目前仅声明 ohos.permission.INTERNET,首页为 Flutter 欢迎页;下文的扫码、相机与蓝牙配对均是产品扩展场景,不表示它们已经接入或在 API 26 上运行。
1. 权限问题应从功能动作倒推
“要做灯牌连接”不是一条足够精确的权限需求。蓝牙扫描、局域网发现、扫码识别、从图库选择二维码、直接调相机拍摄,可能走完全不同的系统接口和授权路径。先把用户动作、实现接口与目标系统版本列成清单,再查所用 Kit 或插件文档中的权限要求。
| 用户动作 | 实际实现尚需确认 | 本文处理方式 |
|---|---|---|
| 打开联网首页 | 是否有网络请求 | 当前配置已有 INTERNET,仍需核对用途 |
| 扫码配对 | 调系统扫码能力还是自建相机流 | 按选定接口文档确认权限 |
| 从系统图库选图 | 系统选择器还是自行读取媒体库 | 不预先假定要广泛媒体权限 |
| 蓝牙发现设备 | 具体连接 Kit、API 与插件 | 未选型前不添加蓝牙权限 |
华为 访问控制概述 强调最小必要、与业务场景一致,以及用户触发时再申请用户授权类权限。旧版文档中的示例配置文件名可能与当前 Stage 工程不同;本文的工程配置位置以实际 module.json5 为准。

2. 先读当前宿主,不要凭印象增配
参考工程的配置非常短:声明了联网,没有相机、位置、蓝牙或媒体权限。这只能证明当前清单如此,不能证明 Android 端、第三方插件或未来业务不需要权限。迁移时要同时盘点 Dart 依赖、鸿蒙插件、宿主 module.json5 与真实功能入口。
// ohos/entry/src/main/module.json5 当前片段
"requestPermissions": [
{ "name": "ohos.permission.INTERNET" },
]
这段来自参考工程现状,不是建议把所有灯牌应用都限制为一个权限。使用 Remote Communication Kit 时,华为 开发准备文档 还给出特定接口可能需要的网络信息权限示例;是否需要,应以实际采用的 API 为准。

3. 静态声明解决的是“应用需要什么”
Stage 工程在模块配置中列出权限。对于需要用户授权的权限,声明通常还要提供对应的用途文字与使用场景;是否属于用户授权、开放范围和字段要求,应看目标 SDK 的当前权限列表。下面以“应用自己打开相机取景”作为条件性示例,不适用于仅调用系统选择器的路线。
// 仅当选定的相机接口确实要求 CAMERA 时添加
"requestPermissions": [
{ "name": "ohos.permission.INTERNET" },
{
"name": "ohos.permission.CAMERA",
"reason": "$string:camera_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
}
]
reason 应描述真实动作,比如“用于扫描灯牌设备上的配对码”,而不是笼统写“提升体验”。when 和权限类型需按目标 API 文档复核;声明成功也不代表用户已经允许。具体扫码场景可参考华为 扫码应用实践 的相机权限配置,但不能把它机械复制到不同扫码实现中。
4. 授权应出现在用户理解原因的时刻
若相机权限属于用户授权类,启动时一口气弹出所有权限会让用户不知道为何需要。应在“扫码配对”按钮之后申请,并在得到明确允许结果后才创建相机资源。华为访问控制原则也要求用户拒绝时,其他不依赖该权限的功能继续可用。
点击“扫码配对”
→ 解释为什么需要相机
→ 系统授权流程
→ 已授权:进入取景/识码
→ 拒绝:停留原页,提供手输设备码入口
→ 系统错误:显示可重试提示,记录错误码
这段是交互时序,不是 SDK 方法签名。具体 ArkTS 申请接口需按项目所用 SDK 版本实现;Flutter 页面还要由插件或平台通道把结果传回 Dart 层。不能只让原生层弹窗,却不把“拒绝/失败”回传给按钮状态。
5. Flutter 层需要一个明确的结果协议
跨平台应用最容易出现“Android 插件有处理,鸿蒙插件返回空值后页面仍进入下一步”。先定义结果语义,再把平台细节藏在对应插件里。以下 Dart 代码是可移植的业务层示意,不是参考工程现有实现。
enum PairCodeResultType { scanned, denied, cancelled, unavailable }
class PairCodeResult {
const PairCodeResult(this.type, {this.value});
final PairCodeResultType type;
final String? value;
}
String pairCodeMessage(PairCodeResult result) {
switch (result.type) {
case PairCodeResultType.scanned:
return '已读取设备码,请核对后连接';
case PairCodeResultType.denied:
return '未获得相机权限,可手动输入设备码';
case PairCodeResultType.cancelled:
return '已取消扫码';
case PairCodeResultType.unavailable:
return '当前设备暂不支持扫码,可手动输入';
}
}
这里将“拒绝”与“设备不支持”拆开,UI 才能给出正确替代入口。平台层必须保证 scanned 才带有效 value;业务层还要校验设备码格式,不能把任何扫描结果直接当作可连接设备。
6. 用户拒绝后,不能把全应用卡死
当扫码权限被拒,手动输入配对码、编辑灯牌内容、浏览已有草稿等不依赖相机的路径应保持可用。若用户永久拒绝,也不应反复触发系统弹窗。可以提示到系统设置检查权限,但提示必须由用户主动点击相关功能触发,不应把“去设置授权”做成唯一主路径。
bool canOpenPairing(PairCodeResult result) {
return result.type == PairCodeResultType.scanned &&
result.value != null &&
result.value!.trim().isNotEmpty;
}
bool canUseManualEntry(PairCodeResult result) {
return !canOpenPairing(result);
}
输入被拒绝、取消或设备不可用时都允许走手输入口;扫码返回空值也能回到手输入口,不能继续连接。真实产品还需把“扫码成功但码格式错误”作为独立提示,避免把识别到的网页链接、普通文本误当成设备码。
7. 不要永久缓存“已授权”这个布尔值
用户可以在系统设置里撤销权限。若应用把一次授权结果写进本地存储,下一次进入扫码页时跳过检查,实际调用仍可能失败。更可靠的顺序是:每次进入受保护能力前检查当前授权状态,必要时按当前平台规则请求,并处理调用过程中权限变化。华为 访问控制概述 也明确提醒不要把曾经授予的状态当作永久事实。
错误路径:上次允许 → 持久化 allowed=true → 永不复查 → 设置撤权后崩溃
正确路径:用户触发 → 查询当前状态 → 申请或使用 → 处理失败/取消
这条规则同样适用于插件接口。即便 Dart 层保存了“准备连接”的页面状态,真正调用受限能力前,鸿蒙原生层仍要核验权限和系统能力。
8. 用最小脚本盘点声明与依赖
大工程里权限往往散在多个模块和插件内。先列出配置文件位置,再逐个对照业务功能、平台通道和第三方插件文档。以下 PowerShell 只做文本定位,不解析 JSON5,也不能证明某项权限实际被使用。
$root = 'D:\lesson\lesson59'
Get-ChildItem -LiteralPath "$root\ohos" -Recurse -Filter module.json5 |
Select-Object -ExpandProperty FullName
rg -n 'requestPermissions|ohos\.permission' "$root\ohos"
rg -n 'MethodChannel|EventChannel|permission|camera|bluetooth' "$root\lib" "$root\pubspec.yaml"
若 rg 找不到 Dart 侧调用,不代表原生插件完全无权限需求;还要检查依赖源码和生成注册表。反过来,字符串出现“camera”也不意味着已经使用了相机 API。把搜索结果当线索,最终以接口调用和官方说明定案。
9. 版本迁移要跑三种授权状态
API 迁移不能只在“此前已授权”的开发机上验证。至少准备新安装未授权、用户主动拒绝、授权后撤销三种状态;同时对旧目标环境和新目标环境执行同一操作路径。若 Android 与鸿蒙两端实现不同,分别保留平台结果,不能用 Android 授权行为推断鸿蒙。
| 状态 | 触发动作 | 应观察到的结果 |
|---|---|---|
| 首次未授权 | 点扫码配对 | 合理说明后进入系统授权流程 |
| 用户拒绝 | 再点扫码 | 不进相机;手输入口仍可用 |
| 设置中撤权 | 返回应用再扫码 | 重新核验,不依赖旧缓存 |
| 设备没有所需能力 | 点扫码 | 明确提示并提供替代路径 |
此处是待执行的验收矩阵,不是本参考工程的测试结果。文案、弹窗行为及返回码应按当前 SDK、真实插件和目标设备记录。
10. 常见误判与交付清单
| 现象 | 先看哪里 | 不宜直接做的事 |
|---|---|---|
| 配置后仍报权限不足 | 实际调用接口、用户授权结果 | 盲目增加更多权限 |
| Android 正常、鸿蒙无弹窗 | 鸿蒙插件/平台通道和声明 | 用 Android 结论代替鸿蒙 |
| 用户拒绝后无法操作 | 是否有替代入口 | 强迫用户授权才能进首页 |
| 审核质疑权限用途 | reason、功能入口、实际行为 | 写泛化的“优化体验” |
| 版本升级后授权路径变化 | 当前 API 文档与设备结果 | 沿用旧版示例签名 |
交付前应确认:每项权限都能对应一个真实用户动作和已选接口;用户授权类权限有准确用途和合适时机;拒绝、撤销、取消、设备不可用分别有回退;旧新系统均有记录。权限迁移的目标不是把清单写长,而是让功能和用户控制保持一致。
参考资料:访问控制概述、Remote Communication Kit 开发准备、扫码应用实践。权限名、授权方式和 API 签名请按实际目标 SDK 的最新文档核对。
更多推荐




所有评论(0)