华为开发者平台接入检测与开放式测试功能汇总

2025-06-20 17:25:25
152次阅读
0个评论
最后修改时间:2025-07-21 23:45:18

一、接入检测(Self-Check)

文档链接:​​文档中心​核心目的:在应用正式提交审核前,通过自动扫描软件包提前发现问题,提高上架通过率。

1. 服务介绍
  • 解决问题:避免因审核不通过导致反复修改,节省时间成本。
  • 检测范围:扫描软件包中的合规性问题(如第三方广告 SDK、DRM SDK 配置、HMS SDK 集成等),HarmonyOS 应用还会检测崩溃 SDK、分析 SDK 等关键组件。
2. 前提条件
  • 已在 AppGallery Connect 创建项目并添加应用。
3. 操作流程
  1. 创建自检任务
  • 登录平台,进入项目下的应用,选择 “质量> 接入检测”,点击 “创建自检”。
  • 填写应用分类、付费情况、发布国家 / 地区,上传 APK/AAB 包(≤4GB,>100MB 需用指定浏览器上传)。
  1. 查看检测报告
  • 检测完成后,在 “检测记录” 中查看结果,报告包含检查项、状态及修改建议(如 DRM SDK 配置、HMS 资源文件等)。
  1. 删除检测记录
  • 在记录列表中点击 “删除” 可移除历史检测数据。
4. 报告示例

| 检查项 | 说明 | 结果 | 修改建议 | | ------------- | ------------------- | -- | -------------------------- | | 第三方广告 SDK | 禁止使用第三方广告 SDK | 通过 | - | | 集成 DRM SDK 配置 | 检查 Manifest 及资源文件配置 | 通过 | 需正确配置 meta-data、activity 等 |

5. 关联服务
  • 与云测试互补:接入检测侧重合规性审核,云测试侧重设备兼容性、性能等运行时问题。

二、开放式测试(Open Test)

文档链接:​​文档中心​核心目的:在应用正式上架前,定向发布测试版本给指定用户,收集反馈并优化。

1. 服务介绍
  • 测试范围:测试版本仅对指定用户可见,支持小范围体验与问题反馈,不面向公开市场。
2. 使用场景
场景分类 场景说明
正式上架前的小范围测试 邀请新人用户提前体验,避免全网上架后因 Bug 导致评分低,影响商业成功。
多团队远程协作测试 跨地域团队通过定向分发测试版本,解决网络限制导致的版本传递问题。
金融银行类应用央行审核 定向分发给央行测试人员,满足审核要求的同时避免现网风险。
3. 核心特点
  • 定向分发:测试版本仅对受邀用户可见,其他用户无法在应用市场搜索到。
  • 反馈收集:用户体验后直接反馈问题,助力优化应用质量。
4. 与其他测试的对比
测试类型 开放式测试 邀请测试(前文) 公开测试(前文)
用户范围 指定信任用户 小范围友好用户(≤1 万) 全网用户(≤1000 万次下载)
可见性 仅受邀用户可见 测试专区 + 分享链接 测试专区 / 公开分享
核心场景 上架前定向优化、审核 功能验证、小范围反馈 大规模用户测试

三、功能关联与应用建议

  1. 接入检测与开放式测试的配合
  • 先通过接入检测确保软件包合规性,再通过开放式测试进行实际用户体验测试,形成 “合规性 + 用户体验” 的完整优化流程。
  1. 适用阶段
  • 接入检测:应用上架前的审核准备阶段。
  • 开放式测试:合规性通过后,正式发布前的用户反馈收集阶段。
  1. 特殊场景应用
  • 金融类应用:先通过接入检测满足合规要求,再用开放式测试定向提交央行审核,确保安全性与合规性双重达标。

四、关键问题快速索引

  1. 接入检测支持哪些应用类型?
  • 支持 Android 应用(APK)和 HarmonyOS 应用 / 元服务(AAB),HarmonyOS 应用会额外检测关键 SDK 集成。
  1. 开放式测试的用户如何获取测试版本?
  • 受邀用户在接受邀请后,可通过华为应用市场直接下载测试版本,或通过开发者提供的分享链接下载。
  1. 接入检测报告中的 “告警” 会影响上架吗?
  • 告警项通常不影响上架,但可能影响功能完整性(如未集成分析 SDK 不影响上架,但无法获取质量数据),建议按建议优化。
import http from '@ohos.net.http';
import common from '@ohos.app.ability.common';
import promptAction from '@ohos.promptAction';
import fileio from '@ohos.fileio';

@Entry
@Component
struct AppTestingCenter {
  // 接入检测状态
  @State selfCheckStatus: string = '未检测';
  // 检测报告内容
  @State selfCheckReport: Array<CheckItem> = [];
  // 开放式测试用户列表
  @State openTestUsers: Array<TestUser> = [];
  // 测试版本列表
  @State testVersions: Array<TestVersion> = [];
  // 当前操作状态
  @State currentAction: string = '';
  // 用户导入文件路径
  @State importFilePath: string = '';
  // 新用户信息
  @State newUser: TestUser = new TestUser();

  // 页面构建
  build() {
    Column() {
      // 标题区域
      this.buildHeader()
      
      // 主内容区
      Tabs({ barPosition: BarPosition.Start }) {
        // 接入检测标签页
        TabContent() {
          this.buildSelfCheckTab()
        }.tabBar('接入检测')
        
        // 开放式测试标签页
        TabContent() {
          this.buildOpenTestTab()
        }.tabBar('开放式测试')
      }
      .barWidth('100%')
      .barHeight(50)
      .width('100%')
      .height('85%')
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F5F7FA')
  }

  // 构建标题
  @Builder
  buildHeader() {
    Row() {
      Text('应用测试中心')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .fontColor(Color.White)
      
      Blank()
      
      Button('刷新')
        .type(ButtonType.Circle)
        .icon($r('app.media.ic_refresh'))
        .backgroundColor('#00000000')
        .onClick(() => this.refreshData())
    }
    .padding(20)
    .width('100%')
    .backgroundColor('#007DFF')
  }

  // 构建接入检测标签页
  @Builder
  buildSelfCheckTab() {
    Column() {
      // 检测状态卡片
      Row() {
        Text(`检测状态: ${this.selfCheckStatus}`)
          .fontSize(18)
          .fontColor(this.selfCheckStatus === '通过' ? '#07C160' : 
                    this.selfCheckStatus === '失败' ? '#FF3B30' : '#FF9500')
        
        Blank()
        
        Button('开始检测')
          .type(ButtonType.Capsule)
          .backgroundColor('#007AFF')
          .enabled(this.selfCheckStatus !== '检测中')
          .onClick(() => this.startSelfCheck())
      }
      .padding(15)
      .width('100%')
      .backgroundColor(Color.White)
      .borderRadius(12)
      .shadow({ radius: 6, color: '#00000010' })
      
      // 检测报告区域
      if (this.selfCheckReport.length > 0) {
        Text('检测报告')
          .fontSize(20)
          .fontWeight(FontWeight.Medium)
          .margin({ top: 20, bottom: 10 })
          .alignSelf(HorizontalAlign.Start)
        
        List({ space: 10 }) {
          ForEach(this.selfCheckReport, (item, index) => {
            ListItem() {
              this.buildReportItem(item, index)
            }
          })
        }
        .height('60%')
      } else if (this.selfCheckStatus === '检测中') {
        LoadingProgress()
          .width(50)
          .height(50)
          .margin({ top: 50 })
      } else {
        Text('请上传应用包进行合规性检测')
          .fontSize(16)
          .margin({ top: 50 })
      }
      
      // 历史记录按钮
      if (this.selfCheckStatus !== '未检测') {
        Button('查看历史记录')
          .width('80%')
          .height(45)
          .margin({ top: 20 })
          .backgroundColor('#34C759')
          .onClick(() => this.viewHistory())
      }
    }
    .padding(20)
    .width('100%')
    .height('100%')
  }

  // 构建报告项
  @Builder
  buildReportItem(item: CheckItem, index: number) {
    Column() {
      // 检查项标题
      Row() {
        Text(`${index + 1}. ${item.name}`)
          .fontSize(16)
          .fontWeight(FontWeight.Medium)
        
        Blank()
        
        Text(item.status)
          .fontColor(item.status === '通过' ? '#07C160' : 
                    item.status === '失败' ? '#FF3B30' : '#FF9500')
      }
      .margin({ bottom: 5 })
      
      // 检查详情
      Text(item.description)
        .fontSize(14)
        .fontColor('#666')
        .margin({ bottom: 8 })
      
      // 修改建议
      if (item.suggestion && item.status !== '通过') {
        Text(`建议: ${item.suggestion}`)
          .fontSize(14)
          .fontColor('#1890FF')
          .margin({ top: 5 })
      }
    }
    .padding(15)
    .width('100%')
    .backgroundColor('#F9F9F9')
    .borderRadius(12)
  }

  // 构建开放式测试标签页
  @Builder
  buildOpenTestTab() {
    Column() {
      // 测试版本管理
      Text('测试版本管理')
        .fontSize(20)
        .fontWeight(FontWeight.Medium)
        .margin({ bottom: 15 })
        .alignSelf(HorizontalAlign.Start)
      
      if (this.testVersions.length > 0) {
        List({ space: 10 }) {
          ForEach(this.testVersions, (version) => {
            ListItem() {
              this.buildVersionItem(version)
            }
          })
        }
        .height('30%')
      } else {
        Text('暂无测试版本')
          .fontSize(16)
          .margin({ bottom: 20 })
      }
      
      Button('创建新测试版本')
        .width('80%')
        .height(45)
        .margin({ bottom: 20 })
        .backgroundColor('#007AFF')
        .onClick(() => this.createTestVersion())
      
      // 测试用户管理
      Text('测试用户管理')
        .fontSize(20)
        .fontWeight(FontWeight.Medium)
        .margin({ bottom: 15 })
        .alignSelf(HorizontalAlign.Start)
      
      // 添加用户表单
      Row() {
        TextInput({ placeholder: '邮箱/手机号' })
          .layoutWeight(2)
          .height(45)
          .fontSize(16)
          .onChange(value => this.newUser.account = value)
        
        TextInput({ placeholder: '姓名' })
          .layoutWeight(1)
          .height(45)
          .margin({ left: 10 })
          .fontSize(16)
          .onChange(value => this.newUser.name = value)
        
        Button('添加')
          .layoutWeight(1)
          .height(45)
          .margin({ left: 10 })
          .onClick(() => this.addTestUser())
      }
      .width('100%')
      .margin({ bottom: 15 })
      
      // 批量导入
      Row() {
        Button('批量导入')
          .width('40%')
          .height(45)
          .backgroundColor('#34C759')
          .onClick(() => this.importUsers())
        
        if (this.importFilePath) {
          Text(`已选择: ${this.importFilePath.split('/').pop()}`)
            .fontSize(14)
            .margin({ left: 10 })
        }
      }
      .width('100%')
      .margin({ bottom: 15 })
      
      // 用户列表
      if (this.openTestUsers.length > 0) {
        List({ space: 8 }) {
          ForEach(this.openTestUsers, (user, index) => {
            ListItem() {
              Row() {
                Text(`${index + 1}. ${user.name}`)
                  .fontSize(16)
                Text(`(${user.account})`)
                  .fontSize(14)
                  .fontColor('#666')
                  .margin({ left: 8 })
                Blank()
                Button('移除')
                  .fontSize(12)
                  .backgroundColor('#FF3B30')
                  .onClick(() => this.removeUser(index))
              }
              .padding(10)
              .width('100%')
            }
          })
        }
        .height('25%')
        .border({ width: 1, color: '#EEE' })
        .borderRadius(8)
      } else {
        Text('暂无测试用户')
          .fontSize(16)
          .margin({ top: 10 })
      }
      
      // 邀请按钮
      if (this.openTestUsers.length > 0) {
        Button('发送测试邀请')
          .width('80%')
          .height(45)
          .margin({ top: 20 })
          .backgroundColor('#AF52DE')
          .onClick(() => this.sendInvitations())
      }
    }
    .padding(20)
    .width('100%')
    .height('100%')
  }

  // 构建版本项
  @Builder
  buildVersionItem(version: TestVersion) {
    Row() {
      Column() {
        Text(`版本: ${version.versionName}`)
          .fontSize(16)
          .fontWeight(FontWeight.Medium)
        Text(`状态: ${version.status}`)
          .fontSize(14)
          .fontColor(version.status === '测试中' ? '#07C160' : '#FF9500')
      }
      
      Blank()
      
      Column() {
        Text(`用户数: ${version.userCount}`)
          .fontSize(14)
        Text(version.expireDate)
          .fontSize(12)
          .fontColor('#999')
      }
      
      Button('管理')
        .width(60)
        .height(30)
        .margin({ left: 10 })
        .onClick(() => this.manageVersion(version))
    }
    .padding(10)
    .width('100%')
    .backgroundColor('#F9F9F9')
    .borderRadius(8)
  }

  // === 功能方法 ===
  
  // 刷新数据
  private refreshData() {
    this.selfCheckStatus = '未检测';
    this.selfCheckReport = [];
    this.loadTestUsers();
    this.loadTestVersions();
  }
  
  // 开始接入检测
  private async startSelfCheck() {
    this.selfCheckStatus = '检测中';
    
    try {
      // 模拟检测过程
      await new Promise(resolve => setTimeout(resolve, 2000));
      
      // 模拟检测结果
      this.selfCheckReport = [
        {
          name: '第三方广告SDK检测',
          description: '检查是否包含禁止使用的广告SDK',
          status: '通过',
          suggestion: ''
        },
        {
          name: 'DRM SDK配置检查',
          description: '验证manifest中的DRM配置是否正确',
          status: '警告',
          suggestion: '缺少meta-data配置项,请在manifest中添加<metadata name="drm_config" value="..."/>'
        },
        {
          name: 'HMS核心服务集成',
          description: '检查HMS Core SDK版本及资源文件',
          status: '通过',
          suggestion: ''
        },
        {
          name: '隐私声明合规性',
          description: '验证隐私政策链接和用户同意流程',
          status: '失败',
          suggestion: '缺少用户同意弹窗,需在应用启动时添加隐私协议确认'
        },
        {
          name: 'HarmonyOS SDK分析',
          description: '检测崩溃分析SDK集成情况',
          status: '通过',
          suggestion: ''
        }
      ];
      
      // 判断整体状态
      const hasFailure = this.selfCheckReport.some(item => item.status === '失败');
      this.selfCheckStatus = hasFailure ? '失败' : '通过';
      
    } catch (error) {
      console.error('检测失败:', error);
      this.selfCheckStatus = '检测失败';
    }
  }
  
  // 查看历史记录
  private viewHistory() {
    promptAction.showToast({ message: '打开历史检测记录', duration: 2000 });
  }
  
  // 加载测试用户
  private loadTestUsers() {
    // 模拟数据
    this.openTestUsers = [
      { account: 'tester1@company.com', name: '张测试' },
      { account: 'tester2@company.com', name: '李验证' },
      { account: 'tester3@company.com', name: '王质量' }
    ];
  }
  
  // 加载测试版本
  private loadTestVersions() {
    // 模拟数据
    this.testVersions = [
      { 
        versionName: 'v2.1.0-beta', 
        status: '测试中', 
        userCount: 15, 
        expireDate: '2025-08-31' 
      },
      { 
        versionName: 'v2.0.1-rc', 
        status: '已结束', 
        userCount: 32, 
        expireDate: '2025-07-15' 
      }
    ];
  }
  
  // 添加测试用户
  private addTestUser() {
    if (!this.newUser.account || !this.newUser.name) {
      promptAction.showToast({ message: '请填写完整用户信息', duration: 2000 });
      return;
    }
    
    this.openTestUsers.push({...this.newUser});
    this.newUser = new TestUser();
    promptAction.showToast({ message: '用户添加成功', duration: 2000 });
  }
  
  // 移除用户
  private removeUser(index: number) {
    this.openTestUsers.splice(index, 1);
  }
  
  // 导入用户
  private importUsers() {
    // 实际项目中调用文件选择器
    promptAction.showToast({ message: '打开文件选择器', duration: 2000 });
    
    // 模拟导入
    setTimeout(() => {
      this.importFilePath = 'internal://cache/users.csv';
      this.openTestUsers.push(
        { account: 'finance1@bank.com', name: '银行审核员A' },
        { account: 'finance2@bank.com', name: '银行审核员B' }
      );
      promptAction.showToast({ message: '成功导入2个用户', duration: 2000 });
    }, 1000);
  }
  
  // 创建测试版本
  private createTestVersion() {
    router.pushUrl({
      url: 'pages/CreateTestVersionPage'
    });
  }
  
  // 管理版本
  private manageVersion(version: TestVersion) {
    promptAction.showActionMenu({
      title: `管理版本: ${version.versionName}`,
      buttons: [
        { text: '查看详情' },
        { text: '添加用户' },
        { text: '停止测试' },
        { text: '取消' }
      ]
    }).then(result => {
      if (result.index === 0) {
        promptAction.showToast({ message: '打开版本详情', duration: 2000 });
      } else if (result.index === 1) {
        promptAction.showToast({ message: '添加测试用户', duration: 2000 });
      } else if (result.index === 2) {
        version.status = '已结束';
        promptAction.showToast({ message: '测试已停止', duration: 2000 });
      }
    });
  }
  
  // 发送邀请
  private sendInvitations() {
    promptAction.showDialog({
      title: '发送测试邀请',
      message: `确定向${this.openTestUsers.length}位用户发送测试邀请吗?`,
      buttons: [{ text: '发送' }, { text: '取消' }]
    }).then(result => {
      if (result.index === 0) {
        promptAction.showToast({ message: '邀请已发送', duration: 2000 });
        
        // 实际项目中调用AGC API发送邀请
        // this.sendRealInvitations();
      }
    });
  }
  
  // 实际发送邀请
  private async sendRealInvitations() {
    try {
      const httpRequest = http.createHttp();
      const response = await httpRequest.request(
        "https://connect-api.cloud.huawei.com/api/open-test/invite",
        {
          method: http.RequestMethod.POST,
          header: { 
            'Content-Type': 'application/json',
            'Authorization': 'Bearer <您的开发者令牌>'
          },
          extraData: JSON.stringify({
            appId: '您的应用ID',
            users: this.openTestUsers
          })
        }
      );
      
      const result = JSON.parse(response.result as string);
      if (result.ret.code === 0) {
        promptAction.showToast({ message: '邀请发送成功', duration: 3000 });
      } else {
        promptAction.showToast({ message: `发送失败: ${result.ret.msg}`, duration: 3000 });
      }
    } catch (error) {
      console.error('邀请发送失败:', error);
      promptAction.showToast({ message: '网络错误,请重试', duration: 2000 });
    }
  }
}

// 数据模型
class CheckItem {
  name: string = '';         // 检查项名称
  description: string = '';  // 检查描述
  status: string = '';       // 检查状态(通过/警告/失败)
  suggestion: string = '';   // 修改建议
}

class TestUser {
  account: string = '';      // 用户账号(邮箱/手机)
  name: string = '';         // 用户姓名
}

class TestVersion {
  versionName: string = '';  // 版本名称
  status: string = '';       // 测试状态
  userCount: number = 0;     // 测试用户数
  expireDate: string = '';   // 有效期
}

`##HarmonyOS应用测试##商务##

收藏00

登录 后评论。没有帐号? 注册 一个。