【听见课堂 HarmonyOS NEXT 实战系列 31】CameraPicker 拍照接入:从权限边界到 resultUri

课堂扫描看起来只是“点一下拍照,再拿到一张图”,真正落地时却要处理 UIAbilityContext、系统相机返回值、用户取消、空 URI、设备能力差异,以及项目自身的权限策略。把这些分支全部塞进 ArkUI 页面,后续 OCR、相册导入和错误恢复就会越来越难维护。

听见课堂把系统拍照入口封装在 ScanCaptureService.capturePhoto() 中,页面只负责展示处理状态并把成功 URI 交给 OCR。本文按当前源码拆解这条链路,同时说明项目实现与当前官方 CameraPicker 最小权限建议之间的差异。

系统相机、ScanCaptureService、OCR 与人工复核之间的数据流

一、为什么首版选择系统 CameraPicker

扫描板书的首版目标是获得一张由用户主动确认的照片,不是自建取景、曝光、对焦和录像系统。CameraPicker 直接拉起系统相机,应用无需维护复杂 Camera Kit 会话,取消和确认也沿用系统交互。

这减少了首版代码量,也把相机预览生命周期留给系统。当前华为官方 FAQ 同样把 CameraPicker 描述为系统提供交互页面、由用户主动完成拍摄与确认的入口。

二、页面只传 UIAbilityContext

页面先从 UI 上下文取得宿主 Context:

const hostContext: Context | undefined = this.getUIContext().getHostContext();
if (hostContext === undefined) {
  throw new Error('UIAbility context is unavailable.');
}
const context: common.UIAbilityContext = hostContext as common.UIAbilityContext;
const uri: string = await this.scanService.capturePhoto(context);

系统 Picker 需要在 UIAbility 场景中拉起界面。页面不拼接文件路径,也不直接解析相机返回对象,只提供必要上下文。

三、PickerProfile 用对象字面量初始化

项目当前写法是:

const profile: cameraPicker.PickerProfile = {
  cameraPosition: camera.CameraPosition.CAMERA_POSITION_BACK
};

这不是纯风格选择。项目曾在真机遇到对 new PickerProfile() 返回对象赋值时的只读属性异常,随后改为对象字面量。对于跨 SDK 版本的配置对象,应优先按当前 API 类型一次性构造,并以真实设备输出校验。

四、媒体类型只请求 PHOTO

result = await cameraPicker.pick(
  context,
  [cameraPicker.PickerMediaType.PHOTO],
  profile
);

板书 OCR 不需要视频,因此媒体类型只包含 PHOTO。能力范围越小,返回处理越清晰,也避免把录像时长、音频权限和大文件管理引入扫描首版。

五、异常和业务失败必须分开

cameraPicker.pick() 抛异常,代表系统调用阶段没有正常完成;返回对象但 resultCode !== 0,则是有结构化返回但业务结果失败。项目分别处理:

try {
  result = await cameraPicker.pick(context, mediaTypes, profile);
} catch (error) {
  throw new Error('camera picker exception: ' + this.formatError(error));
}

if (result.resultCode !== 0) {
  throw new Error('camera picker failed, resultCode=' + result.resultCode);
}

两类错误若统一写成“拍照失败”,日志和用户恢复路径都会失去依据。

六、resultUri 要先去空格再判断

项目读取并归一化 URI:

const uri: string = result.resultUri.trim();
if (uri.length === 0) {
  throw new Error('camera picker returned empty uri.');
}
return uri;

空字符串不是成功结果。若继续把它交给 ImageSource,错误会被推迟到解码阶段,页面只能看到与真实原因无关的 OCR 失败。

七、日志不打印完整图片 URI

当前日志只记录 resultCode、媒体类型和是否存在 URI:

hilog.info(DOMAIN, TAG,
  'CameraPicker resultCode=%{public}d mediaType=%{public}s hasUri=%{public}s',
  result.resultCode, result.mediaType, uri.length > 0 ? 'true' : 'false');

图片 URI 可能包含用户文件位置或媒体标识。诊断只需知道是否拿到 URI,不应默认把完整地址写入公开日志。

八、用户取消不能写入课堂记录

页面把空 URI 视为取消:

if (uri.length === 0) {
  this.scanStatus = '已取消拍照,未读取任何图片';
  return;
}

取消不是 OCR 失败,更不是一条空扫描记录。状态文案明确“未读取任何图片”,也不会触发 Repository 写入。

九、项目当前权限策略需要如实说明

ScanCaptureService 当前在调用 Picker 前检查并请求 ohos.permission.CAMERAmodule.json5 也声明该权限。这是项目现状,文章不能删去这条事实。

但当前华为 CameraPicker FAQ 说明:系统相机交互由用户主动完成时,应用开发者可以不申请相机权限。因此这里应区分“项目当前实现”与“平台当前最小要求”。是否移除项目权限门禁,需要结合目标 API、既有真机回归和上架隐私说明单独评估,不能只改文章不改代码。

十、为什么不直接把 resultUri 存入数据库

图片 URI 在当前流程中只用于临时预览和本机 OCR。保存时写入的是复核后的文字与来源,页面完成后清空 scanImageUri

长期保存外部选择器 URI 还要处理访问期限、原图删除、迁移和隐私说明。首版没有这些需求,就不应把临时句柄当作永久业务数据。

十一、系统相机失败后的三条恢复路

P05 页面保留:

  1. 再次尝试拍照;
  2. 从系统图库选择一张已有图片;
  3. 使用本地示例继续体验 OCR 校对和保存闭环。

第三条只能标为“本地示例”,不能冒充真实拍照或真实 OCR 成功。

拍照成功、取消、权限/系统失败与降级入口的状态矩阵

十二、页面用 isScanProcessing 防重复点击

if (this.isScanProcessing) {
  return;
}
this.isScanProcessing = true;
try {
  // 打开系统相机并处理结果
} finally {
  this.isScanProcessing = false;
}

系统 UI 拉起期间重复点击可能创建并发流程。幂等门禁同时用于按钮 enabled,让视觉状态和业务状态一致。

十三、错误文本要映射成恢复动作

页面通过 scanFailureMessage() 识别权限、取消、resultCode 和普通异常,再追加“检查权限、从图库选择或使用本地示例”等动作。用户需要的不是堆栈,而是下一步。

底层仍保留序列化后的 code/name/message,用于区分设备、系统能力和业务错误。

十四、当前验证证据到哪一级

项目契约脚本、HAP 构建、模拟器安装和 P05 降级路径曾通过;模拟器没有可用相机,真实拍照成功、返回 URI、真图预览和 OCR 仍不能由这组证据证明。后续真机 HDC 又曾被 USB 安全通道阻塞。

因此本文能确认“链路已实现并可构建、失败可恢复”,不能写成“所有真机拍照已验证通过”。

十五、验收清单

至少应覆盖:后置相机正常拍照、用户取消、权限策略、系统相机不可用、非零 resultCode、空 URI、连续点击、页面切后台、图片解码失败,以及完成复核后数据库不保留临时 URI。

还要在目标 SDK 上复核 CameraPicker 的权限最小化方案,确保 manifest、运行时行为和隐私声明一致。

十六、总结

CameraPicker 接入的核心不是一行 pick(),而是把系统 UI、返回协议、临时资源和恢复路径组织成可验证流程。听见课堂把相机调用收口在 Service 中,页面只消费 URI 和状态;真正需要继续补齐的是目标真机成功证据与当前权限策略复核。

下一篇继续讨论:即使项目当前相机权限门禁被拒绝,扫描功能为什么仍不能整体失效。

参考:华为 CameraPicker 常见问题、项目 ScanCaptureService.ets、P05/P06 扫描 OCR 复验报告。

Logo

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

更多推荐