HarmonyOS 5开发从入门到精通(二十):应用发布与上架

HarmonyOS 5开发从入门到精通(二十):应用发布与上架

本章将完整介绍HarmonyOS应用从打包签名到上架应用市场的全流程,帮助开发者顺利完成应用发布。

一、核心概念

1. 应用签名机制

HarmonyOS通过数字证书(.cer文件)和Profile文件(.p7b文件)等签名信息来保证应用的完整性。应用上架到华为应用市场必须通过签名校验,这是应用安全分发的基础保障。

2. 应用分发流程

HarmonyOS应用的分发遵循严格的审核和发布流程,包括应用信息配置、软件包上传、审核提交、版本管理等环节,确保应用符合华为应用市场的质量标准。

二、关键API详解

1. 构建配置API

// build-profile.json5配置示例
{
  "app": {
    "signingConfigs": [
      {
        "name": "release",
        "material": {
          "certpath": "signing/release.cer",
          "storePassword": "yourpassword",
          "keyAlias": "release",
          "keyPassword": "yourpassword",
          "profile": "signing/release.p7b",
          "signAlg": "SHA256withECDSA",
          "storeFile": "signing/release.p12"
        }
      }
    ]
  }
}

2. 发布前检查API

import bundleManager from '@ohos.bundle.bundleManager';
import permission from '@ohos.permission';

class AppPublishChecker {
  // 检查权限声明
  static async checkRequiredPermissions(): Promise<string[]> {
    const requiredPerms = [
      permission.Permission.INTERNET,
      permission.Permission.READ_MEDIA
    ];
    const missingPerms: string[] = [];
    const bundleInfo = await bundleManager.getBundleInfoForSelf();
    
    for (const perm of requiredPerms) {
      if (!bundleInfo.reqPermissions.includes(perm)) {
        missingPerms.push(perm);
      }
    }
    return missingPerms;
  }
}

3. 自动化构建API

// 终端构建命令
./gradlew assembleRelease

// 或者使用图形界面
// Build > Build HAP(s)/APP(s) > Build Release HAP

4. 包体规范检查

// 包体大小限制检查
class PackageSizeChecker {
  static readonly MAX_APP_SIZE = 2 * 1024 * 1024 * 1024; // 2GB
  static readonly MAX_HAP_SIZE = 20 * 1024 * 1024; // 20MB
  
  static checkPackageSize(packagePath: string): boolean {
    const stats = fs.statSync(packagePath);
    return stats.size <= this.MAX_APP_SIZE;
  }
}

三、实战案例

完整发布流程示例

import http from '@ohos.net.http';
import fs from '@ohos.file.fs';

// 应用发布管理器
class AppPublisher {
  private static readonly AGC_API = 'https://connect-api.cloud.huawei.com';
  
  // 一键发布流程
  static async publishApp(appId: string, hapPath: string): Promise<boolean> {
    try {
      console.log('开始应用发布流程...');
      
      // 1. 运行预检查
      const precheck = await this.runPrePublishChecks(hapPath);
      if (!precheck.passed) {
        console.error('预检查未通过:', precheck.details);
        return false;
      }
      
      // 2. 上传HAP文件
      console.log('上传应用包文件中...');
      const uploadResult = await this.uploadPackage(appId, hapPath);
      
      // 3. 提交审核
      console.log('提交应用审核...');
      const reviewResult = await this.submitForReview(appId, uploadResult.versionId);
      
      console.log('应用发布流程完成,跟踪ID:', reviewResult.trackingId);
      return true;
      
    } catch (error) {
      console.error('发布流程出错:', error);
      return false;
    }
  }
  
  // 发布前检查
  private static async runPrePublishChecks(hapPath: string): Promise<any> {
    const checks = {
      packageSize: PackageSizeChecker.checkPackageSize(hapPath),
      permissions: await AppPublishChecker.checkRequiredPermissions(),
      signature: await this.verifySignature(hapPath)
    };
    
    return {
      passed: checks.packageSize && checks.permissions.length === 0 && checks.signature,
      details: checks
    };
  }
  
  // 上传应用包
  private static async uploadPackage(appId: string, hapPath: string): Promise<any> {
    const token = await this.getAGCToken();
    const httpRequest = http.createHttp();
    
    try {
      const file = await fs.open(hapPath, fs.OpenMode.READ_ONLY);
      const fileStat = await fs.stat(hapPath);
      const fileContent = await fs.read(file.fd, { length: fileStat.size });
      await fs.close(file.fd);
      
      const response = await httpRequest.request(
        `${this.AGC_API}/api/v1/apps/${appId}/packages`,
        {
          method: 'POST',
          header: {
            'Authorization': `Bearer ${token}`,
            'Content-Type': 'multipart/form-data'
          },
          extraData: {
            'file': {
              filename: hapPath.split('/').pop(),
              contentType: 'application/octet-stream',
              data: fileContent
            }
          }
        }
      );
      
      return JSON.parse(response.result);
    } catch (error) {
      throw new Error(`上传应用包失败: ${error}`);
    }
  }
}

// 发布状态监控组件
@Entry
@Component
struct PublishStatusMonitor {
  @State appId: string = '';
  @State trackingId: string = '';
  @State status: string = 'pending';
  @State lastUpdate: string = '';
  
  private timer: number = 0;
  
  onPageShow() {
    const params = router.getParams();
    if (params) {
      this.appId = params.appId;
      this.trackingId = params.trackingId;
      this.startStatusPolling();
    }
  }
  
  onPageHide() {
    this.stopStatusPolling();
  }
  
  private startStatusPolling(): void {
    this.timer = setInterval(() => {
      this.checkStatus();
    }, 300000); // 每5分钟检查一次
  }
  
  private stopStatusPolling(): void {
    if (this.timer) {
      clearInterval(this.timer);
    }
  }
  
  private async checkStatus(): Promise<void> {
    try {
      const httpRequest = http.createHttp();
      const token = await this.getAGCToken();
      
      const response = await httpRequest.request(
        `https://connect-api.cloud.huawei.com/api/v1/apps/${this.appId}/reviews/${this.trackingId}`,
        {
          method: 'GET',
          header: {
            'Authorization': `Bearer ${token}`
          }
        }
      );
      
      const result = JSON.parse(response.result);
      this.status = result.status;
      this.lastUpdate = new Date().toLocaleString();
      
    } catch (error) {
      console.error('获取审核状态失败:', error);
      this.status = 'error';
    }
  }
  
  build() {
    Column({ space: 20 }) {
      Text('应用审核状态')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
      
      Text(`应用ID: ${this.appId}`)
        .fontSize(16)
      
      Text(`跟踪ID: ${this.trackingId}`)
        .fontSize(16)
      
      Divider()
      
      if (this.status === 'pending') {
        LoadingProgress()
        Text('审核中,请耐心等待...')
          .fontSize(16)
          .fontColor(Color.Blue)
      } else {
        Text(`状态: ${this.getStatusText()}`)
          .fontSize(20)
          .fontColor(this.getStatusColor())
        
        Text(`最后更新: ${this.lastUpdate}`)
          .fontSize(14)
          .fontColor(Color.Gray)
      }
      
      Button('手动刷新')
        .width(200)
        .onClick(() => {
          this.checkStatus();
        })
    }
    .width('100%')
    .height('100%')
    .padding(20)
  }
  
  private getStatusText(): string {
    const statusMap: Record<string, string> = {
      'pending': '审核中',
      'approved': '审核通过',
      'rejected': '审核未通过',
      'error': '获取状态失败'
    };
    return statusMap[this.status] || this.status;
  }
  
  private getStatusColor(): Color {
    const colorMap: Record<string, Color> = {
      'pending': Color.Blue,
      'approved': Color.Green,
      'rejected': Color.Red,
      'error': Color.Orange
    };
    return colorMap[this.status] || Color.Black;
  }
}

签名文件生成流程

// 密钥生成配置类
class KeyStoreGenerator {
  static generateKeyStore(config: {
    storeFile: string,
    password: string,
    alias: string,
    validity: number, // 年数
    organization: string,
    countryCode: string
  }): void {
    // 在DevEco Studio中执行:
    // Build > Generate Key and CSR
    // 按照向导填写相应信息
  }
}

// 发布证书申请流程
class CertificateManager {
  static async applyReleaseCertificate(csrFile: string): Promise<string> {
    // 在AGC控制台操作:
    // 用户与访问 > 证书管理 > 新增证书
    // 选择"发布证书"类型,上传CSR文件
    return "certificate.cer";
  }
}

四、常见问题与解决方案

1. 签名相关问题

问题: "HarmonyOS hapAppProvision文件非法"

  • 原因: Profile文件与当前应用不匹配
  • 解决: 检查Profile文件中的bundle-name是否与待发布应用包名一致

问题: "证书和Profile不匹配"

  • 原因: 发布证书与Profile文件中使用的证书不一致
  • 解决: 确保打包时使用的证书与申请Profile时使用的证书一致

2. 包体规范问题

问题: 软件包大小超限

  • 限制: 主包≤2GB,智慧屏/手表HAP包≤20MB
  • 解决: 优化资源文件,使用动态加载

3. 审核被拒处理

// 重新提交审核
static async resubmitAfterRejection(
  appId: string,
  trackingId: string,
  fixes: {
    updatedHapPath?: string,
    updatedPrivacyPolicy?: string
  }
): Promise<boolean> {
  // 根据审核反馈进行修复后重新提交
  return true;
}

五、总结

关键知识点

  • 应用签名是HarmonyOS应用上架的必要条件
  • 发布证书和Profile文件必须匹配且有效
  • 包体大小和内容规范需要严格遵守
  • 审核流程需要耐心等待和及时响应

🔧 核心流程

  1. 准备阶段: 生成密钥 → 申请证书 → 配置签名
  2. 构建阶段: 编译打包 → 包体检查 → 预发布测试
  3. 提交阶段: 上传软件包 → 填写应用信息 → 提交审核
  4. 发布阶段: 审核跟踪 → 问题修复 → 正式上架

💡 最佳实践建议

  1. 提前规划: 证书有效期建议设置25年以上
  2. 测试充分: 使用真机测试所有功能,特别是权限相关功能
  3. 材料准备: 提前准备好应用截图、图标、隐私政策等材料
  4. 关注规范: 严格遵守华为应用市场的设计和内容规范
  5. 及时响应: 审核期间保持关注,及时处理审核反馈

通过本系列教程的学习,你已经掌握了从HarmonyOS开发入门到应用上架的全流程技能。继续实践和探索,你将能够开发出更加优秀的HarmonyOS应用!

posted @ 2025-12-23 21:37  奇崽  阅读(2)  评论(0)    收藏  举报