HarmonyOS趣味相机实战第23篇:module.json5权限清单、Release构建与隐私自检

摘要

本地调试能拍照,不代表 Release 包可以安全发布。相机应用在上架前还要核对 module 权限理由、使用场景、启动资源、备份扩展、SDK 兼容、代码严格模式、混淆策略和签名凭据。配置中一个不一致的字段,可能导致审核疑问、深色模式启动闪烁、备份行为超出预期或敏感凭据进入仓库。

本文基于 D:/APP/1quweixiangji 趣味相机工程,对 AppScope/app.json5entry/src/main/module.json5build-profile.json5、资源文件和测试目录做一次发布前复盘。重点是建立可自动执行的自检清单,而不是只在 DevEco Studio 中点击一次构建。文中不会展示任何真实证书、密码、用户数据或本地登录信息。

工程背景与配置定位

文件 责任 发布前关注点
AppScope/app.json5 bundle、版本、图标和应用名 版本递增与品牌一致性
entry/src/main/module.json5 Ability、页面、权限和扩展 最小权限与组件导出
entry/src/main/resources/base/element/string.json 权限理由和模块文案 用户可理解且与用途一致
entry/src/main/resources/base/element/color.json 启动窗口背景 颜色模式一致
entry/src/main/resources/dark/element/color.json 深色资源 强制 light 时检查闪烁
entry/src/main/resources/base/profile/backup_config.json 备份恢复开关 数据范围与隐私说明
build-profile.json5 SDK、产品、签名和严格模式 凭据治理与版本基线
entry/build-profile.json5 release 构建和混淆 包体和代码保护
entry/src/testentry/src/ohosTest 自动化测试 发布门槛

当前版本与构建边界

项目 当前值 说明
bundleName com.fun.quweixiangji 稳定应用标识
versionName 1.0.4 用户可见版本
versionCode 1000004 发布时必须递增
target SDK 6.0.2(22) 当前目标 API
compatible SDK 6.0.2(22) 当前兼容基线
runtimeOS HarmonyOS 构建目标
module type entry 主模块
deviceTypes phone 当前只支持手机
installationFree false 非免安装

HarmonyOS趣味相机发布前自检链路

一、版本号必须形成单一事实来源

应用配置:

{
  "app": {
    "bundleName": "com.fun.quweixiangji",
    "vendor": "fun-camera",
    "versionCode": 1000004,
    "versionName": "1.0.4",
    "icon": "$media:layered_image",
    "label": "$string:app_name"
  }
}

发布前检查:

  • versionCode 高于线上版本。
  • versionName 与发布说明一致。
  • 构建产物实际解析出的版本与配置一致。
  • 测试包和正式包不共享容易混淆的版本号。
  • 文章、截图和隐私文本不写死旧版本。

可以让 CI 读取 JSON5 后对比基线,阻止重复 versionCode。

二、bundleName不能在渠道间随意改变

bundleName 是应用身份,不是显示名称。修改后会被系统视为不同应用,升级链路、数据目录和签名关系都可能变化。

显示名称来自资源:

{
  "name": "EntryAbility_label",
  "value": "轻拍水印相机"
}

品牌改名优先调整 label 和上架素材,不要为了改桌面名称修改 bundleName。

三、module.json5只声明真正使用的权限

当前权限:

{
  "requestPermissions": [
    {
      "name": "ohos.permission.CAMERA",
      "reason": "$string:camera_permission_reason",
      "usedScene": {
        "abilities": ["EntryAbility"],
        "when": "inuse"
      }
    }
  ]
}

这与当前功能一致:应用只需要相机实时取景与拍照,不应为了未来可能的功能提前申请位置、麦克风或通讯录。

最小权限原则:

源码存在真实调用
  -> 产品确实需要
  -> module声明
  -> 运行时在用户动作后申请
  -> 隐私说明同步

四、权限理由要描述用途而不是技术名词

当前资源:

{
  "name": "camera_permission_reason",
  "value": "用于实时取景和拍照保存本地水印照片"
}

文案包含能力、目的和本地使用场景,比“需要相机权限”更清楚。

应与应用内前置说明一致:

需要访问相机用于实时取景和拍照保存。
确认后会打开系统权限申请。

系统弹窗理由、应用内说明和隐私政策不能互相矛盾。

五、usedScene与真实生命周期对应

when: inuse 表示使用期间。工程应做到:

  • 冷启动不自动打开相机。
  • 用户进入拍摄流程后才请求。
  • 页面离开或 Ability 后台时停止 CameraSession。
  • 不在后台持续采集。
  • 权限撤销后不盲目重启。

声明只是契约,代码生命周期必须实现同样边界。

六、Ability导出范围需要审查

主 Ability:

{
  "name": "EntryAbility",
  "srcEntry": "./ets/entryability/EntryAbility.ets",
  "exported": true,
  "skills": [
    {
      "entities": ["entity.system.home"],
      "actions": ["ohos.want.action.home"]
    }
  ]
}

作为桌面入口需要相应 skills。其他内部扩展默认不应导出:

{
  "name": "EntryBackupAbility",
  "type": "backup",
  "exported": false
}

每个新增 Ability 或 Extension 都要回答:外部应用是否需要调用?如果不需要,保持 exported: false

七、备份开关不是无条件勾选项

{
  "allowToBackupRestore": true
}

开启前必须确认:

  • 哪些 Preferences 会进入恢复范围。
  • 真实照片文件是否包含。
  • 水印地点和备注是否属于用户数据。
  • 删除数据后是否可能再次恢复。
  • 跨版本 schema 如何迁移。
  • 隐私说明是否覆盖系统备份行为。

onBackup()onRestore() 被调用不等于数据恢复正确,必须做真机计数、引用和媒体可访问性验证。

八、启动窗口资源与颜色模式一致

基础资源:

{
  "name": "start_window_background",
  "value": "#FFFFFF"
}

深色资源为黑色,而 EntryAbility 当前强制 light。发布前在系统深色模式冷启动,确认是否出现黑色启动窗口后突然变白。

选择一种明确策略:

  1. 完整支持 light/dark,页面资源同步变化。
  2. 产品固定 light,启动资源也保持一致。

不要让资源限定词与运行时强制配置相互打架。

九、严格模式属于发布质量门槛

产品配置开启:

{
  "strictMode": {
    "caseSensitiveCheck": true,
    "useNormalizedOHMUrl": true
  }
}

收益:

  • import 路径大小写问题在构建期暴露。
  • 模块 URL 使用规范格式。
  • Windows 上能构建但其他环境失败的概率降低。
  • 依赖路径更容易审计。

不要为了临时通过构建关闭 strictMode,应修复源码路径和依赖声明。

十、资源复制策略需要理解

entry 构建:

{
  "resOptions": {
    "copyCodeResource": {
      "enable": false
    }
  }
}

发布前确认运行时是否依赖动态读取源码目录中的非标准资源。贴纸未来若从代码资源目录加载,关闭复制后可能只在开发环境存在、安装包中缺失。

正确做法是把正式图片放入标准 resources,或明确配置需要复制的资源,而不是依赖工程目录偶然可见。

十一、Release混淆不能永久关闭

当前 release 配置中混淆未启用。开发阶段便于定位,但发布前应评估:

  • 包体大小。
  • ArkTS 代码可读性暴露。
  • 反射、动态路由和资源名兼容。
  • 日志堆栈可定位性。
  • 混淆规则是否保护框架入口和序列化字段。

启用流程:

先在候选Release包启用
  -> 运行完整测试
  -> 检查EntryAbility和页面路由
  -> 检查Preferences模型字段
  -> 检查CameraKit回调
  -> 保存映射文件用于问题定位

不能在正式提交前最后一刻首次开启。

十二、签名凭据不能进入源码仓库

构建配置可能引用证书、Profile 和密钥。发布工程必须做到:

  • 密码不以明文提交。
  • 证书私钥文件不进入公共仓库。
  • 本地绝对路径不作为团队唯一配置。
  • CI 使用受控 Secret 注入。
  • Debug 与 Release 签名隔离。
  • 凭据轮换有流程。
  • 日志不打印密钥、密码或完整签名参数。

可以提交无敏感值的模板:

{
  "name": "release",
  "material": {
    "certpath": "${RELEASE_CERT_PATH}",
    "profile": "${RELEASE_PROFILE_PATH}",
    "storeFile": "${RELEASE_STORE_PATH}"
  }
}

实际注入方式按 DevEco Studio 和团队构建系统确定。

十三、发现明文凭据后的处理顺序

仅从最新文件删除不够,因为 Git 历史可能仍存在。处理:

立即停止继续传播
  -> 轮换受影响凭据
  -> 从当前配置移除
  -> 评估并清理版本历史
  -> 更新忽略规则
  -> 引入Secret扫描
  -> 重新验证签名链路

最重要的是先轮换。历史清理不能让已经泄露的旧密钥重新安全。

十四、日志分级与隐私

当前服务使用 hilog。发布前搜索:

JSON.stringify(error)
%{public}s
locationText
note
uri
PixelMap
byteBuffer

错误对象可能包含路径或系统上下文。建议只记录:

interface SafeErrorLog {
  module: string;
  operation: string;
  errorCode?: number;
  recoverable: boolean;
}

不要记录照片字节、人脸坐标、水印地点、备注、Preferences 完整 JSON 或媒体 URI。

十五、权限被拒绝时仍需可用

审核和用户都会测试拒绝权限。预期:

  • 应用不退出。
  • 拍摄页显示明确入口。
  • 相册和文档 Tab 仍可使用。
  • 不循环弹系统权限框。
  • 用户再次主动操作时可重试。
  • 永久拒绝时提供系统设置路径说明。

权限拒绝不是异常崩溃路径,而是必须支持的产品状态。

十六、应用内数据删除闭环

相册支持删除元数据,文档也支持删除。发布前明确:

  • 删除照片是否级联处理文档。
  • 真实媒体文件是否同步删除。
  • Preferences 是否 flush。
  • 删除后备份恢复行为。
  • 用户是否能理解删除范围。

只删除列表记录但保留媒体文件,会造成隐私和存储不一致;只删除文件但保留索引,会留下打不开的卡片。

十七、构建前静态扫描

可在 PowerShell 或 CI 执行:

检查versionCode递增
检查module权限白名单
检查exported组件清单
检查明文password/token/key模式
检查证书私钥扩展名
检查绝对用户路径
检查console/hilog敏感字段
检查资源引用存在
检查测试模板用例

扫描结果应阻止 Release 构建,而不是只产生无人查看的警告。

十八、构建与产物验证

清理构建输出
  -> 执行Release构建
  -> 确认签名成功
  -> 解析产物bundleName和版本
  -> 检查包体大小
  -> 安装到干净测试设备
  -> 首次启动与权限拒绝
  -> 授权后预览和拍照
  -> 前后台和升级安装

不要只验证构建命令退出码。真正要发布的是安装产物,而不是源码目录。

十九、升级安装测试

准备线上旧版本数据:

安装旧版本
  -> 保存照片和文档元数据
  -> 安装新Release包覆盖升级
  -> 检查数据迁移
  -> 检查权限状态
  -> 检查相机预览
  -> 再次冷启动

同时执行卸载重装,区分升级保留数据与全新安装行为。

二十、上架素材一致性

应用包之外还要核对:

  • 应用名称与桌面 label 一致。
  • 图标与包内 layered image 一致。
  • 截图来自当前版本,不显示未实现功能。
  • 功能说明不宣称云同步或永久保存,除非已实现。
  • 权限用途与隐私说明一致。
  • 联系方式和政策链接可访问。
  • 截图不包含真实个人照片和地点。

二十一、自动化配置检查示例

将 JSON5 解析为对象后断言:

interface ReleaseCheckResult {
  errors: string[];
  warnings: string[];
}

function checkPermissions(names: string[]): string[] {
  const allowed = new Set(['ohos.permission.CAMERA']);
  return names.filter(name => !allowed.has(name));
}

检查 exported:

function checkExported(
  components: Array<{ name: string; exported: boolean }>
): string[] {
  return components
    .filter(item => item.exported && item.name !== 'EntryAbility')
    .map(item => `unexpected exported component: ${item.name}`);
}

Secret 扫描不要把匹配到的完整值打印到日志,只报告文件和字段类型。

二十二、真机发布验收矩阵

场景 操作 预期
全新安装 首次启动拒绝相机 应用可继续使用非拍摄功能
权限同意 进入拍摄页 预览正常且理由一致
深色系统 冷启动 无明显黑白闪烁
后台 Home后等待 相机资源释放
前台恢复 返回拍摄页 重新检查并恢复预览
升级安装 覆盖旧版本 Preferences数据兼容
备份恢复 测试设备迁移 数据范围符合说明
无前置镜头 切换操作 降级且不崩溃
Release混淆 完整主流程 路由、模型和回调正常

二十三、常见问题排查

现象 可能原因 排查方向
审核认为权限理由不清 文案只有技术名词 描述功能、目的和使用时机
深色模式启动闪烁 dark资源与强制light冲突 统一颜色策略
Release路由失败 混淆规则缺少保留项 检查Ability和页面入口
CI无法签名 配置依赖本机绝对路径 使用环境注入和团队路径
仓库扫描发现密码 明文凭据进入配置 轮换并清理历史
权限拒绝后反复弹窗 冷启动自动申请 改为用户动作触发
删除后数据又出现 备份/索引策略不一致 核对删除与恢复边界
安装包缺少贴纸资源 依赖未复制代码资源 使用标准resources或显式配置

二十四、上线前验收清单

  • versionCode 已递增且产物解析一致。
  • bundleName 与线上应用保持一致。
  • 权限清单只包含实际使用能力。
  • CAMERA 理由与应用内说明、隐私政策一致。
  • 相机只在 inuse 生命周期运行。
  • 仅桌面入口 Ability 对外导出。
  • 备份范围经过真机验证并写入说明。
  • 启动窗口与颜色模式一致。
  • strictMode 保持开启。
  • 正式资源全部进入构建产物。
  • Release混淆在候选包提前验证。
  • 签名密码和私钥不在源码仓库。
  • 已运行 Secret 和敏感日志扫描。
  • 权限拒绝、撤销和恢复流程可用。
  • 删除照片、文档和媒体边界明确。
  • Release产物完成全新安装和升级安装。
  • 截图、名称、图标与当前版本一致。

总结

HarmonyOS 相机应用发布不是一次构建命令,而是一组可验证契约:版本与身份稳定、权限最小且理由清楚、生命周期符合 inuse、备份范围可解释、资源进入产物、Release 策略经过真机验证、签名凭据不进入源码。

把这些规则固化成静态扫描、构建产物解析和真机矩阵后,发布质量不再依赖最后一次人工检查。尤其是权限、日志、备份和签名四类敏感边界,应在开发阶段持续验证,而不是收到审核问题后再补救。

Logo

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

更多推荐