讨论权限闸门之前,先问一个具体问题:用户首次拒绝相机权限后,按钮仍持续弹出请求,前台恢复后又错误沿用之前的授权结果?答案不能只从界面效果判断。展会签到台的二维码核验入口有真实操作顺序,必须从触发、运算、反馈三个环节理解。

技术主题封面

真正需要回答的问题

用户看到“开始扫码”就点击,这是合理预期;对应用来说却有两个前置判断:当前设备与运行环境是否有可用相机能力,以及应用是否已经得到本次访问所需的授权。前者需要能力检查,后者需要系统授权接口,不能用一个按钮颜色代替。

“权限属于随时可能变化的系统状态,必须在执行受保护操作之前重新确认”是这篇最重要的设计判断。单看成功路径,展会签到台的二维码核验入口可以靠若干事件回调工作;但只要用户中途修改、取消或重复操作,若没有清晰约束,就无法确定最终输出究竟属于哪一次输入。工程拆分必须以这些不确定性为起点。

我们希望得到的不是一张孤立的效果图,而是一条可追溯的处理链。整个过程拆为点击扫码、查询授权、允许访问、拒绝说明、设置恢复。每一段只承诺自己负责的结果,下一段不靠猜测前一步的内部状态。对权限闸门而言,这种清晰分工也是回归测试能够写得具体的前提。

权限检查位置应尽量靠近受保护的实际操作。用户可能在系统设置里撤销权限,所以页面第一次打开时检查为 granted,并不能证明十分钟后依然如此。扫码操作之前重新核验,才有机会准确解释失败,而不是突然出现黑屏。

先区分几个容易混淆的概念

实施选择是:UI 展示“准备检查—已授权—被拒绝—前往设置”四种状态,真正调用扫码能力前再判定授权。这里不能倒过来,先把组件和请求写完,再向现有代码里硬塞判断。规则入口只接收必要数据,输出也尽量可序列化。这样在没有设备的环境下仍能对核心逻辑做确定性检查。

当路径执行到“点击扫码”,先区分用户仍在编辑、数据已经校验和动作已经确认三种事实。随后执行“查询授权”时,使用同一份中间结果才能避免旧数据回流。日志或测试也应记录阶段,而不是只看最终页面有没有消失。

把“查询授权”到“允许访问”视作可中断的边界。任何阶段都可能遇到无效输入或设备状态变化,恢复时应回到上一个仍然可信的事实,而不是为了显示流畅强行跳到最终步骤。这样的路径更适合实际调试。

把“允许访问”和“拒绝说明”分成两个阶段,是为了让每个阶段都有可检查的输入与输出。展会签到台的二维码核验入口并不需要复杂框架才能做到这一点,真正需要的是一个稳定的数据契约:前一步产出的值能被下一步清楚识别,失败时也能停在正确的位置。

第一次拒绝并不意味着用户永远不需要扫码。页面应继续提供不依赖相机的帮助信息,并允许用户主动前往系统设置。不要把拒绝理解为服务不可用,也不该循环弹出系统申请框来强迫用户同意。

权限闸门的关键设计

核心做法与代码

下面给出可以独立检查的关键实现。代码只覆盖本篇要证明的核心行为;涉及工程权限、服务端协议或构建设置的条件已在上下文列明。它应被放入明确的模块中,而不是把所有业务逻辑堆进 ArkUI build()。

import { abilityAccessCtrl, common, bundleManager, Permissions } from '@kit.AbilityKit'
import { BusinessError } from '@kit.BasicServicesKit'
const PERMISSION: Permissions = 'ohos.permission.CAMERA'
async function ensureCamera(context: common.UIAbilityContext): Promise<boolean> {
  const manager = abilityAccessCtrl.createAtManager()
  const token = await bundleManager.getBundleInfoForSelf(
    bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION)
  const state = manager.checkAccessTokenSync(token.appInfo.accessTokenId,
    PERMISSION)
  if (state === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) return true
  try {
    const result = await manager.requestPermissionsFromUser(context,
      [PERMISSION])
    return result.authResults[0] === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED
  } catch (error) { return false }
}

系统授权弹窗由系统负责显示,应用不要在其上面叠加遮罩或营销提示。提示文案在点击前说明为什么需要相机,比权限弹出后额外解释更符合用户的决策路径。

权限闸门只有在工程清单声明相机权限后才能用于实际设备。示例 EntryAbility 名称需与本工程保持一致,理由资源也要在资源文件中存在。

// entry/src/main/module.json5 中 module.requestPermissions 字段
"requestPermissions": [
  { "name": "ohos.permission.CAMERA",
    "reason": "$string:camera_reason",
    "usedScene": { "abilities": ["EntryAbility"], "when": "inuse" }
  }
]
// entry/src/main/resources/base/element/string.json 的 string 数组中:
{ "name": "camera_reason", "value": "扫码需要相机访问权限" }

代码中的关键判断来自业务约束,而不是某个屏幕比例。以“允许访问”为界,输入应先满足所属阶段的前置条件;当判断失败时,返回错误或保留状态比直接抛出无法解释的页面状态更可控。对需要 UI 反馈的部分,随后由组件转换为明确的文案。

如果把上述核心逻辑接到一个 ArkUI 页面,推荐组件仅处理状态投影和交互事件:例如 TextInput.onChange 接收新的字符串,Button.onClick 触发一次显式动作,得到结果后再设置 @State 驱动展示。对于不是输入框的权限闸门功能,则采用相同的单向数据边界而不强行引入 TextInput。

权限闸门的运行界面或工程路径

常见误判怎么避开

最应该提前考虑的风险是:不能通过本地偏好保存“已授权”;系统弹窗被遮挡和自动连弹均属于不合适的体验。这句话不是发布时随手附加的注意事项,而是编码时必须坚持的限制。只要异常路径缺乏明确处理,下一次回归就可能把一个本来可恢复的状态变成难以判断的失败。

测试时至少覆盖首次授权、首次拒绝、系统设置中撤销、后台返回、无摄像头设备几种情况。如果只能在预览环境观察 UI,也要明确记录尚未验证的硬件路径,不能以一个示意页就宣布扫码功能已经成功运行。

错误恢复。 在查询授权成功、允许访问尚未完成的位置构造异常。应能说明数据暂存在哪里,用户看到何种反馈,是否允许重新触发,以及什么条件下必须停止。不要为了追求一张好看的页面而隐藏正在等待确认的真实状态。

跨版本核对。 在数据和配置持续演进的前提下,固定一份输入,记录预期输出;随后修改不相关的界面颜色或布局,再运行同一用例。如果结果变化,说明权限闸门与展示层之间出现了不应存在的隐性耦合。

边界值核对。 不只输入一个“正常例子”,还需要空字符串、用户明确的否定值、最大长度附近以及重复内容等不同形态。对权限闸门来说,边界值是否被恰当表达,往往比用户第一次成功完成流程更能反映实现质量。

状态验收。 在展会签到台的二维码核验入口中,专门制造一个尚未完成的状态:用户首次拒绝相机权限后,按钮仍持续弹出请求,前台恢复后又错误沿用之前的授权结果。观察模块是否明确阻止下一动作、是否保留用户已经输入的有效内容,以及重新操作后是否会得到可解释的新结果。这个检查的价值在于不把一次成功操作当作稳定性的全部证据。

可复核的结论

这套闸门只负责授权,不负责实际图像识别。接口边界分明后,未来选择 Camera Kit 或第三方扫码方案不会改变授权状态机;但具体 API 和权限范围需要按最终扫码方案核对。

本篇可用以下结果作为验收目标:拒绝一次、设置页重开权限、返回应用继续扫码的操作链有清晰结果。检验时先从固定输入入手,再加入中途切换、重复操作和失败恢复。可解释的错误结果也属于正确实现的一部分;没有必要为了让每个步骤都显示绿色而悄悄跳过不满足的前置条件。

当方案需要扩展,优先问“新需求将改变哪个阶段”。如果它只改变点击扫码,便不应触及后面的查询授权;如果它改变最终业务语义,规则层和序列化层才需要一起调整。这个决策方式能够避免每加一个按钮就复制一份流程逻辑,也使连载中的代码可以逐篇成长。

对于业务异常,提示文案至少要回答当前状态、可以继续的动作以及不能继续的原因。展会签到台的二维码核验入口里的用户通常不关心内部类名或错误码,但需要知道是等待、重试还是修改输入。把工程错误类别映射为用户可理解的行为建议,比把原始异常堆栈放在对话框里更合适。

交付的验证证据应当与主张相称。纯函数逻辑可以使用固定输入输出来证明;界面效果只能由真实运行或明确标注的示意展示;网络限流、签名、权限等涉及外部系统的结论,需要在对应环境中完成复核。不要把静态检查结果提升为已经完成真实设备测试的结论。

还有一个容易被忽视的协作问题:页面状态、领域规则和最终输出往往由不同代码文件甚至不同开发者维护。要让改动可控,必须为权限闸门明确最小接口,规定哪些数据可以跨模块传递、哪些只是展示层的临时变量;代码评审只要看到两处重复决定同一事实,就需要回到单一来源原则。

权限闸门的扩展场景与异常路径

这类问题的适用边界同样清楚:权限属于随时可能变化的系统状态,必须在执行受保护操作之前重新确认。当具体 SDK、设备能力或服务端协议发生变化,应重新查看对应的官方接口约束,再重新跑上述场景验证,而不是根据一张示例图推断系统行为。

官方资料

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐