HarmonyOS趣味相机实战第23篇:module.json5权限清单、Release构建与隐私自检
HarmonyOS趣味相机实战第23篇:module.json5权限清单、Release构建与隐私自检
摘要
本地调试能拍照,不代表 Release 包可以安全发布。相机应用在上架前还要核对 module 权限理由、使用场景、启动资源、备份扩展、SDK 兼容、代码严格模式、混淆策略和签名凭据。配置中一个不一致的字段,可能导致审核疑问、深色模式启动闪烁、备份行为超出预期或敏感凭据进入仓库。
本文基于 D:/APP/1quweixiangji 趣味相机工程,对 AppScope/app.json5、entry/src/main/module.json5、build-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/test、entry/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 | 非免安装 |

一、版本号必须形成单一事实来源
应用配置:
{
"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。发布前在系统深色模式冷启动,确认是否出现黑色启动窗口后突然变白。
选择一种明确策略:
- 完整支持 light/dark,页面资源同步变化。
- 产品固定 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 策略经过真机验证、签名凭据不进入源码。
把这些规则固化成静态扫描、构建产物解析和真机矩阵后,发布质量不再依赖最后一次人工检查。尤其是权限、日志、备份和签名四类敏感边界,应在开发阶段持续验证,而不是收到审核问题后再补救。
更多推荐



所有评论(0)