一个应用在开发机上能够打开,为什么提交应用市场时还可能因为包名、签名或权限被挡下来?原因并不复杂:本地调试验证的是“能否在某个开发环境中运行”,上架还需要回答另一些问题——它究竟属于哪个已注册应用、用什么证书证明身份、为哪些能力申请了权限、上传包能否被发布系统识别,以及新版本是否仍与旧版本属于同一条升级链。

应用上架与配置一致性技术封面

与其把这些问题留到最后一次点击“发布”时才处理,我更倾向于把上架准备看成一次工程配置审计。本文从一套独立的 Stage 模型应用出发,核对 AppScope/app.json5、entry/src/main/module.json5 和 DevEco Studio 签名设置之间的关系;用一份小型纯 ArkTS/TS 审计逻辑把重复检查显式化。它不能取代应用市场的正式审核,却能让那些可以提前发现的配置错误尽早暴露。

应用身份不是标题,也不是工程目录名

最容易被忽略的配置叫 bundleName。它是系统识别应用身份的重要字段,跟桌面上显示给用户的应用名称不是同一个概念。应用在 AppGallery Connect(AGC)创建时使用的包名,应与工程 app.json5 中的配置保持一致。看起来只是修改一串反向域名字符串,实际却可能导致绑定不到正确的应用记录、签名信息不匹配,甚至影响已经发布应用的更新路径。

版本信息也有两层:面向系统比较的 versionCode 与面向用户展示的 versionName。更新包的版本值应按发布规则递增或调整,不能为了发布说明好看而随手复用旧值。调试阶段可以自由创建测试工程,发布阶段必须有一份冻结后的“预期身份”作为对照,确保打包采用的配置就是审核对应的那份配置。

// AppScope/app.json5 中的关键片段;合并至现有 app 节点
{
  "app": {
    "bundleName": "com.example.readingdesk",
    "versionCode": 12,
    "versionName": "1.2.0"
  }
}

这里的包名与版本是配置结构示例,项目发布时必须替换为 AGC 上已注册的真实身份和本次版本信息。不要根据应用中文名称猜测 bundleName,更不要直接复制一个其他团队的签名证书。团队还应该记录谁拥有发布帐号、谁负责续期证书、哪个分支对应待提交版本,让应用身份的管理与代码改动一样有追溯性。

应用身份、构建配置与签名材料的关系图

最小权限与发布审查是一回事

module.json5 中的 requestPermissions 声明决定应用请求哪些系统权限,但声明本身不保证任何访问场景都获准。权限有普通、需要用户授权及受限能力等不同类型,具体处理方式应根据当期官方权限说明逐项核实。一个只读取本地公开内容的阅读器,不应该为了“将来可能用到”而添加定位、通讯录等权限;一个确实要访问 HTTPS 接口的页面,可以在对应模块声明 ohos.permission.INTERNET,同时在产品功能描述和隐私合规材料中说明实际用途。

// entry/src/main/module.json5 的 module 节点关键片段
{
  "module": {
    "name": "entry",
    "type": "entry",
    "requestPermissions": [
      { "name": "ohos.permission.INTERNET" }
    ]
  }
}

这个片段不表示完整 module.json5,原工程中的 mainElement、abilities、设备类型和资源引用都应继续保留。判断权限是否必要,需要对照代码里真实调用的 Kit/API,而不是只对照 IDE 里有没有红色报错。特别是受限权限及 ACL 权限,涉及资格、Profile 与应用市场审核,不能把调试时的签名成功理解成发布许可已经获得。来自第三方 SDK 的数据收集行为,也需要和应用的隐私声明对应。

设备类型和目标 SDK 同样要审阅。只做过手机上的界面适配,就直接在发布元数据中勾选平板或其他设备,后续可能出现布局、交互、输入方式与权限行为不一致。设备兼容矩阵应由真实支持范围决定,不能把“系统提供了这个 API”简化成“所有设备都能正常使用”。

签名材料必须形成一条闭合的身份链

开发调试签名与发布签名承担的角色不同。DevEco Studio 的自动签名便于开发阶段调试,关联 AGC 已注册应用的签名能力又涉及账号、应用绑定与 Profile 信息;正式分发应根据当前发布方式选用对应证书和 Profile。特别要区分“本地构建完成”与“发布平台接受上传包”:前者是编译及打包事件,后者仍需身份、格式、权限与渠道审核。

与签名有关的文件通常包括密钥、证书、Profile。它们应在受控环境中保管,切忌把私钥口令、.p12 或其他敏感签名材料提交到公开代码仓库。build-profile.json5 中的 signingConfigs 应在 DevEco Studio 项目结构里由受权人员配置,并核对证书、应用包名、允许的能力以及有效期。生产密钥交接需要明确保管责任,人员变动也不应让应用失去维护发布的能力。

一个非常实用的检查顺序是:先核对 AGC 应用记录和工程包名是否一致,再确认本次发布配置引用的是预期签名材料,最后检查权限、版本信息和目标设备。不要反过来在上传失败以后才到处修改包名或临时换证书;这种“凭感觉修好”的操作很容易影响已发布版本的升级连续性。

用一个小型审计器固化低成本检查

发布审计不必一开始就建设庞大的平台。对结构化配置先做纯函数校验,再由构建流程或测试界面显示发现的问题,可以有效减少人工重复漏项。下面定义的数据从工程配置与 AGC 控制台分别整理出来,审计函数只比较已确认的字段,不读取证书私钥,也不会把密码输出到日志。INTERNET 被当作此示例确实需要的唯一权限;换项目时应修改允许清单,而不是照抄这项约束。

// ReleaseAudit.ets:纯逻辑,可用于测试脚本或 ArkTS 页面
export interface ReleaseInput {
  registeredBundle: string;
  configuredBundle: string;
  previousVersionCode: number;
  candidateVersionCode: number;
  requestedPermissions: string[];
  allowedPermissions: string[];
  releaseProfileReady: boolean;
}

export interface AuditItem {
  name: string;
  passed: boolean;
  detail: string;
}

export function auditRelease(input: ReleaseInput): AuditItem[] {
  const identity: boolean = input.registeredBundle.length > 0 &&
    input.registeredBundle === input.configuredBundle;
  const version: boolean = Number.isInteger(input.candidateVersionCode) &&
    input.candidateVersionCode > input.previousVersionCode;
  const permissions: boolean = input.requestedPermissions.every(
    (item: string): boolean => input.allowedPermissions.includes(item));
  return [
    { name: '应用身份', passed: identity,
      detail: identity ? '包名一致' : '核对 AGC 应用记录与 app.json5' },
    { name: '版本递增', passed: version,
      detail: version ? '版本编码递增' : '检查 versionCode' },
    { name: '权限最小化', passed: permissions,
      detail: permissions ? '声明在允许清单内' : '检查 module.json5 权限' },
    { name: '发布签名', passed: input.releaseProfileReady,
      detail: input.releaseProfileReady ? '已核对发布配置' : '检查证书与 Profile' }
  ];
}

将检查结果映射到 ArkUI 页面时,用简单列表即可让每个问题显性化。这里的 releaseProfileReady 是发布负责人复核后的人工输入标志,不是程序自动识别到签名有效;若要做自动化检测,必须依照构建工具公开接口和组织安全规范扩展。示例特意不存储证书路径或密钥信息,避免把发布安全做成一份到处传播的调试日志。

// pages/Index.ets:与上面的 ReleaseAudit.ets 放在同一模块
import { auditRelease, AuditItem, ReleaseInput } from '../ReleaseAudit';

@Entry
@Component
struct Index {
  private input: ReleaseInput = {
    registeredBundle: 'com.example.readingdesk',
    configuredBundle: 'com.example.readingdesk',
    previousVersionCode: 11,
    candidateVersionCode: 12,
    requestedPermissions: ['ohos.permission.INTERNET'],
    allowedPermissions: ['ohos.permission.INTERNET'],
    releaseProfileReady: false
  };
  private findings: AuditItem[] = auditRelease(this.input);

  build() {
    Column({ space: 14 }) {
      Text('发布配置审计').fontSize(24).fontWeight(FontWeight.Bold)
      List({ space: 10 }) {
        ForEach(this.findings, (item: AuditItem) => {
          ListItem() {
            Row({ space: 12 }) {
              Text(item.passed ? '✓' : '!')
                .fontColor(item.passed ? '#17855C' : '#C65A35')
              Column({ space: 6 }) {
                Text(item.name).fontSize(17)
                Text(item.detail).fontSize(13).fontColor('#607080')
              }
            }.padding(12).width('100%')
          }
        }, (item: AuditItem): string => item.name)
      }.layoutWeight(1)
    }.padding(20).width('100%').height('100%')
  }
}

发布前配置核对的步骤清单与判定结果

提交前的最后一公里,不靠“再试一次”

真正准备交付时,可以从一份冻结版本开始核查:对应分支的源码与依赖锁定结果是否明确;构建模式是不是用于正式发布的配置;AGC 包名、版本和发布签名是否匹配;权限声明是否有功能依据;支持设备、隐私材料和必要的应用介绍是否一致;上传后的有效性检查是否反馈了具体问题。每一项都应该有负责人和可复查的证据,不能因为某一次本地安装成功就整体勾选“完成”。

遇到上传失败,优先读平台报告区分包结构、签名、版本冲突与权限/合规问题,再决定回到哪一层修改。如果只更改一个 versionName 就反复上传,而没有核对包身份或发布证书,问题通常会留在原处。对发布成功的版本也应保管好原签名链与版本记录,为后续更新留下连续证据。

官方参考:DevEco Studio 自动签名、应用身份与 bundleName、应用依赖配置、AppGallery Connect。具体版本与平台要求,以准备提交时对应渠道的发布指南为准。

应用上架不是开发阶段的附加手续,而是一项贯穿代码、身份、安全与维护责任的工程活动。包名保证“它是谁”,权限解释“它要做什么”,证书与 Profile 回答“谁有权交付它”,版本记录确保“它能否被持续更新”。当这些约束在构建之前就被对齐,发布才有机会从临时救火变成可重复执行的流程。

Logo

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

更多推荐