发布前复查不是把 AGC 后台字段重新填一遍,而是把包内配置、入口代码、权限说明、视觉素材和实际运行路径放到同一张表里逐项对齐。晨迹相机这类本地打卡相机应用,看起来功能边界很清楚:启动后进入 pages/Index,拍照或录像需要相机权限,录像需要麦克风权限,数据主要落在本地记录里。但一旦进入 AppGallery 发布流程,审核看到的不是开发者脑内的产品边界,而是包名、版本、设备类型、权限、截图、图标、隐私说明、应用介绍和安装启动表现共同组成的证据链。
这篇文章基于 D:\huawei\one15 的真实源码做一次发布前复查。目标不是讲"怎么点击后台按钮",而是说明如何从 HarmonyOS 工程里提取可复核信息,避免出现常见的上架不一致:AGC 写了手机和平板,包里却没有平板适配;隐私材料说不采集声音,module.json5 却声明了麦克风;截图展示了视频能力,权限原因却只写拍照;应用名、图标、启动页和包内资源不是同一套。发布复查的价值,就在于把这些问题提前挡住。

本文会解决五个具体问题:
- 从
AppScope/app.json5核对包名、版本、图标和应用名。 - 从
module.json5核对设备类型、入口 Ability、权限原因和备份能力。 - 从
EntryAbility.ets核对启动页、系统栏、浅色模式和入口稳定性。 - 从资源目录核对封面截图、启动图标和 AGC 素材一致性。
- 把发布前复查整理成一张能直接执行的检查表。


一、先把"发布材料"还原成源码证据
发布材料最容易出问题的地方,是它们经常被当作运营文案处理。开发者会先写一个好看的应用介绍,再补截图和关键词;但 AppGallery 审核真正关心的是这些材料是否能被包内实现支撑。晨迹相机的复查应该从源码反推,而不是从文案反推。
源码里可以直接确认的第一组信息来自 AppScope/app.json5:
json5
{
"app": {
"bundleName": "com.jiaweikan.one15",
"vendor": "jiaweikan",
"versionCode": 1000000,
"versionName": "1.0.0",
"icon": "$media:layered_image",
"label": "$string:app_name"
}
}
这段配置给出四个发布字段的源头:包名、开发者标识、版本号和图标引用。AGC 后台里任何包名、应用版本、图标素材或应用名称的填写,都不能脱离这里。尤其是 bundleName,它是平台识别应用身份的核心字段;如果后台选错应用,后面的截图、隐私、权限和包体再正确,也会变成另一个应用的材料。
发布复查时我会把这组字段拆成下面的表:
| 复查项 | 源码证据 | 发布侧含义 | 风险 |
|---|---|---|---|
| 包名 | com.jiaweikan.one15 |
AGC 应用、Profile、包体绑定必须一致 | 传错包会导致无法选择或审核材料错位 |
| 版本名 | 1.0.0 |
用户可见版本 | 更新说明必须匹配这个版本 |
| 版本号 | 1000000 |
平台升级判断 | 低于已上架版本会被拒绝 |
| 图标 | $media:layered_image |
包内图标入口 | AGC 图标应与包内视觉一致 |
这里不需要发明额外能力。晨迹相机的文章、截图和介绍可以讲"本地相机打卡、日历记录、心情与水印",因为这些能力能从页面和资源里继续验证;不能把它包装成云相册、多人同步、AI 图像识别或在线社区,因为当前源码没有这些链路。
二、设备范围要跟 module.json5 对齐
HarmonyOS 应用常见的发布风险之一,是设备范围填写过宽。AGC 选择了手机、平板、2in1,包里也声明了这些设备,但页面如果没有适配,审核时仍可能暴露截断、遮挡、系统栏不可读或横竖屏异常。
晨迹相机的 module.json5 明确声明了目标设备:
json5
{
"module": {
"deviceTypes": [
"phone",
"tablet",
"2in1"
],
"pages": "$profile:main_pages",
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:startIcon",
"startWindowBackground": "$color:start_window_background"
}
]
}
}
这段配置至少带来三条复查任务。
第一,AGC 设备类型不能少于真实包支持,也不能大于实际可验证范围。这里源码声明了 phone、tablet 和 2in1,因此截图材料和发布说明最好覆盖这些形态下不会出问题的关键页面。如果 AGC 只选手机,可能浪费包内已声明的范围;如果页面没有实测平板和 2in1,就不能在介绍里强调"大屏深度适配"。
第二,入口页面必须可达。pages 指向 $profile:main_pages,而 main_pages.json 只有一个页面:
json
{
"src": [
"pages/Index"
]
}
这意味着发布复查的启动路径非常明确:安装后启动 EntryAbility,加载 pages/Index,再由 Index.ets 组合产品入口。审核人员不需要复杂账号或外部服务才能进入核心流程。发布说明也应该尽量写成"打开即用,本地记录",而不是要求审核者先配置网络、登录或导入数据。
第三,启动图标和启动背景要一致。startWindowIcon 使用 $media:startIcon,startWindowBackground 使用 $color:start_window_background。资源里该颜色是 #FBF4EA,与入口 Ability 里设置系统栏背景色保持一致。发布截图如果采用同一套暖色背景,包内启动体验和商店材料会更统一。
三、权限复查要写到"使用场景"而不是只写权限名
权限是 AppGallery 审核最敏感的材料之一。晨迹相机声明了两个权限:
json5
{
"requestPermissions": [
{
"name": "ohos.permission.CAMERA",
"reason": "$string:camera_permission_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.MICROPHONE",
"reason": "$string:microphone_permission_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
}
]
}
对应的中文原因在 string.json 中:
json
{
"string": [
{
"name": "camera_permission_reason",
"value": "用于拍摄打卡照片和录制打卡视频"
},
{
"name": "microphone_permission_reason",
"value": "用于录制打卡视频中的声音"
}
]
}
这两段信息决定了隐私政策、权限说明和审核备注的写法。不能只写"需要相机权限",更不能把麦克风解释成"优化体验"。更稳妥的表达应该是:
| 权限 | 真实触发场景 | 用户可理解说明 | 不应出现的说法 |
|---|---|---|---|
CAMERA |
拍摄打卡照片、录制打卡视频 | 用户主动点击拍照或录像时使用 | 后台拍摄、自动识别、持续采集 |
MICROPHONE |
录制打卡视频声音 | 仅在录制视频时采集声音 | 持续监听、语音识别、环境分析 |
如果应用描述里强调"本地打卡相机",隐私材料就要继续说明照片、视频、心情和水印记录的处理边界。当前源码里没有看到账号、云同步、广告、推送或在线社区能力,发布材料就不要增加这些说法。权限材料的核心原则是:只解释真实功能需要的权限,只覆盖真实运行时会发生的数据行为。
四、入口 Ability 决定启动和系统栏复查点
发布审核不只看配置,也会实际安装启动。晨迹相机的入口能力在 EntryAbility.ets:
ts
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
try {
this.context.getApplicationContext().setColorMode(
ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT
);
} catch (err) {
hilog.error(DOMAIN, 'testTag', 'Failed to set colorMode. Cause: %{public}s', JSON.stringify(err));
}
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate');
}
onWindowStageCreate(windowStage: window.WindowStage): void {
AppStorage.setOrCreate<common.UIAbilityContext>('abilityContext', this.context);
windowStage.loadContent('pages/Index', (err) => {
if (err.code) {
hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
return;
}
const mainWindow = windowStage.getMainWindowSync();
mainWindow.setWindowSystemBarEnable(['status', 'navigation']);
mainWindow.setWindowSystemBarProperties({
statusBarColor: '#FBF4EA',
statusBarContentColor: '#2A1B0E',
navigationBarColor: '#FBF4EA',
navigationBarContentColor: '#2A1B0E'
});
});
}
}
这段代码给发布复查提供了几个明确结论。
首先,应用主动锁定浅色模式。发布材料里如果展示深色模式截图,就需要额外证明深色适配;当前更合理的做法,是截图保持浅色视觉,并在发布复查中确认系统深色模式下不会出现反色、低对比或系统栏不可读。这里代码设置了状态栏和导航栏颜色,背景 #FBF4EA 与文字 #2A1B0E 对比明确,适合在手机、平板和 2in1 上检查顶部和底部系统区域。
其次,入口页面失败会记录日志并停止加载。发布前烟测要覆盖安装、启动、进入首页、切换核心 Tab、触发拍照权限、返回、退出和卸载。仅仅能构建 HAP 或 APP 不代表发布可用;如果 loadContent('pages/Index') 失败,用户看到的就是启动异常。
再次,AppStorage 保存了 abilityContext。这个上下文被相机服务或数据服务使用时,发布复查要确认没有在后台静默触发敏感能力。对于晨迹相机,合理路径是用户点击拍照或录像后请求权限,而不是启动即申请权限。
五、应用名、图标、启动图和截图必须是一套视觉
源码里 label 和 EntryAbility_label 都指向资源字符串,值为"晨迹相机"。图标侧则使用 layered_image、startIcon.png 和一批页面内素材。发布材料应维持三个一致:
| 材料 | 源码位置 | 复查方法 |
|---|---|---|
| 应用名称 | string.json 的 EntryAbility_label |
AGC 名称、截图标题、文章标题一致 |
| 包内图标 | AppScope/app.json5 的 $media:layered_image |
检查透明背景、暗色可读性、启动页一致性 |
| 启动图标 | module.json5 的 $media:startIcon |
安装启动时与商店图标不冲突 |
| 截图素材 | resources/base/media |
展示真实页面和真实能力 |
晨迹相机的资源目录包含 img_camera_hero.png、img_camera_preview.png、img_home_daily.png、img_memory_cover.png、img_watermark_life.png 等真实业务图片,也包含 ic_tab_home.png、ic_tab_camera.png、ic_profile_album.png 等功能图标。AGC 截图应该优先来自真实页面或忠实复现的页面状态,而不是用外部模板图代替。原因很简单:截图一旦展示了不存在的入口、活动、云同步或 AI 能力,审核和用户都会认为应用过度宣传。
封面截图也要遵循同一原则。本文发布到 CSDN 草稿时使用当前文章目录的 media/cover.png,但 AppGallery 上架材料不能直接拿技术文章封面当应用截图。应用商店截图应展示晨迹相机真实 UI:首页打卡、相机入口、日历记录、心情统计、水印选择或回收站等页面。技术文章封面适合社区内容,商店截图适合产品展示,两者不能混用。
六、备份能力和本地数据声明要一起看
module.json5 声明了一个备份扩展能力:
json5
{
"extensionAbilities": [
{
"name": "EntryBackupAbility",
"srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets",
"type": "backup",
"exported": false,
"metadata": [
{
"name": "ohos.extension.backup",
"resource": "$profile:backup_config"
}
]
}
]
}
而 backup_config.json 中写的是:
json
{
"allowToBackupRestore": true
}
这会影响隐私和数据声明。晨迹相机可以被描述为以本地记录为主,但不能简单写成"不会发生任何备份或恢复相关处理",因为包内存在备份恢复配置。更准确的复查方式是区分三类数据:
| 数据类型 | 源码表现 | 发布材料写法 |
|---|---|---|
| 打卡记录 | 页面和数据服务维护本地记录 | 说明用于本机打卡记录展示 |
| 拍照/录像权限 | 用户触发拍摄或录像时使用 | 说明权限触发场景和用途 |
| 备份恢复 | allowToBackupRestore: true |
隐私材料需覆盖系统备份恢复场景 |
如果后续决定把应用定位为完全本地离线,就要继续检查是否存在网络权限、HTTP 请求、账号、统计 SDK、推送 SDK 或 Web 组件。当前已经看到的 module.json5 没有声明 INTERNET 权限,这支持"本地为主"的发布描述;但备份能力仍然应纳入材料,而不是忽略。
七、发布前检查不能跳过安装启动烟测
AppGallery 复查最终要落到可执行动作。基于当前源码,晨迹相机的发布前烟测可以写成下面这组步骤:
text
1. 使用 release 配置构建 HAP 或 APP。
2. 确认包名为 com.jiaweikan.one15,版本为 1.0.0 / 1000000。
3. 安装到目标设备或模拟器,覆盖 phone、tablet、2in1 中实际发布范围。
4. 启动 EntryAbility,确认进入 pages/Index。
5. 检查状态栏和导航栏颜色是否可读。
6. 点击拍照入口,确认只在用户动作后请求 CAMERA。
7. 点击录像入口,确认 MICROPHONE 说明只对应录像声音。
8. 取消授权、取消拍摄、返回首页,确认页面不崩溃。
9. 查看日历、记忆、个人页等核心页面,确认截图展示的能力真实存在。
10. 卸载应用,确认没有异常残留流程阻塞。
这组步骤不需要把所有业务细节测完,但要覆盖审核最关心的稳定性入口:安装、启动、权限、核心功能、返回和卸载。尤其是权限弹窗,必须确认触发时机与材料一致。如果应用一启动就弹相机或麦克风权限,而文案说"仅在拍摄时使用",这就是材料和行为不一致。
八、把 AGC 字段做成可追溯清单
实际发布时,我建议为每个版本保留一张复查表。晨迹相机这一版可以这样填写:
| AGC 字段 | 建议值或证据 | 复查状态 |
|---|---|---|
| 应用名称 | 晨迹相机,来自 EntryAbility_label |
待后台核对 |
| 包名 | com.jiaweikan.one15 |
源码已确认 |
| 版本 | 1.0.0 / 1000000 |
源码已确认 |
| 设备类型 | phone、tablet、2in1 | 包内已声明,需真机或预览验证 |
| 权限 | CAMERA、MICROPHONE | 源码已声明,需隐私材料覆盖 |
| 权限原因 | 拍照/录像、录像声音 | 资源字符串已确认 |
| 图标 | $media:layered_image、startIcon.png |
需视觉检查透明度和一致性 |
| 启动页 | pages/Index |
源码已确认,需启动烟测 |
| 数据说明 | 本地打卡记录,备份恢复开启 | 需隐私材料同步 |
| 网络能力 | 未见 INTERNET 权限 | 不能宣传云能力 |
这张表的作用是防止发布阶段靠记忆。只要后来改了版本号、权限、设备类型或截图,就必须同步更新这张表。否则很容易出现"包里已经改了,后台没改"或"后台补了新能力,源码没实现"的问题。
九、常见问题和处理方式
| 问题 | 可能原因 | 处理方式 |
|---|---|---|
| AGC 无法选择新包 | 包名、证书、Profile 或版本号不匹配 | 先核对 bundleName、versionCode、签名 Profile 绑定关系 |
| 审核认为权限说明不足 | 权限原因写得太泛 | 按拍照、录像声音等真实场景改写隐私和权限说明 |
| 平板截图被退回 | 包声明了 tablet/2in1,但截图或布局未覆盖 | 补充目标设备截图,或缩小发布设备范围 |
| 启动页和商店图标不一致 | layered_image、startIcon、AGC 图标不是同一设计 |
统一图标来源并重新检查亮暗背景 |
| 说明中出现云同步或 AI 能力 | 运营文案超过源码事实 | 删除未实现能力,只保留源码可复核功能 |
| 深色模式下系统栏不可读 | 应用锁浅色但系统栏/背景没同步 | 检查 setWindowSystemBarProperties 和页面根背景 |
这些问题的共同点是:它们通常不是代码编译错误,而是"材料和包内行为不一致"。发布复查的目标就是把这种不一致提前发现。
十、可以直接复用的发布前复查函数
如果团队里有多个 HarmonyOS 应用,可以把发布复查固化成一个轻量脚本或人工清单。下面是一个伪代码版本,表达检查思路:
ts
interface ReleaseProfile {
bundleName: string;
versionName: string;
versionCode: number;
deviceTypes: string[];
permissions: string[];
entryPage: string;
appLabel: string;
}
function assertReleaseMaterial(profile: ReleaseProfile): string[] {
const problems: string[] = [];
if (profile.bundleName.length === 0) {
problems.push('缺少 bundleName,无法确认 AGC 应用身份');
}
if (profile.versionCode <= 0) {
problems.push('versionCode 不合法,可能无法升级发布');
}
if (!profile.deviceTypes.includes('phone')) {
problems.push('未声明 phone,需要确认发布设备范围');
}
if (profile.permissions.includes('ohos.permission.MICROPHONE') &&
!profile.permissions.includes('ohos.permission.CAMERA')) {
problems.push('录像类能力通常需要同时复查相机与麦克风说明');
}
if (profile.entryPage !== 'pages/Index') {
problems.push('入口页与 main_pages 配置不一致');
}
return problems;
}
这段代码不是要替代 AGC 后台校验,而是提醒团队把发布复查变成稳定输入。发布前只要能把 ReleaseProfile 填出来,就能马上发现字段缺失、权限不成对、入口页不一致等基础问题。
十一、验证清单
发布前我会按下面的顺序验证晨迹相机:
| 验证项 | 方法 | 通过标准 |
|---|---|---|
| 本地构建 | 执行项目构建命令 | 无编译错误,生成目标包 |
| 包名版本 | 读取 AppScope/app.json5 |
与 AGC 应用和版本一致 |
| 入口页面 | 启动 EntryAbility |
正常进入首页,无白屏 |
| 权限弹窗 | 手动触发拍照/录像 | 权限只在用户动作后出现 |
| 设备适配 | phone/tablet/2in1 预览或真机 | 无截断、错位、系统栏不可读 |
| 图标素材 | 检查 package 图标和 AGC 图标 | 同一设计,非透明风险可控 |
| 隐私材料 | 对照权限和备份配置 | 不遗漏 CAMERA、MICROPHONE、备份恢复 |
| 截图材料 | 对照真实页面 | 不展示未实现功能 |
这个清单里有些项可以从源码静态确认,有些必须靠真机或模拟器验证。比如包名版本可以读 JSON,入口页可以读配置,但系统栏可读性、权限弹窗时机、平板布局和卸载表现必须实际运行。
十二、总结
晨迹相机这次 AppGallery 发布复查的核心结论是:源码已经提供了足够清晰的发布证据。AppScope/app.json5 给出包名、版本和图标;module.json5 给出设备范围、权限、入口 Ability 和备份扩展;EntryAbility.ets 给出启动页、浅色模式和系统栏配置;资源目录给出图标、页面图片和截图素材来源。真正需要控制的风险,是不要让 AGC 材料超过这些证据。
更稳的发布方式,是把"应用描述、权限说明、隐私政策、截图、图标、设备范围、版本号"都回到源码逐项核对。这样做虽然比直接填后台慢一点,但能减少审核阶段的返工。发布复查不是最后的形式流程,而是 HarmonyOS 应用从工程代码走向真实用户前的一次一致性校验。
部分内容由AI辅助生成