第37篇:检测应用更新组件——check_app_update

在这里插入图片描述

一、引言

应用更新是保持应用活力的重要功能,它让开发者能够及时修复 Bug、添加新功能、优化用户体验。DriverLicenseExam 项目的 check_app_update 组件封装了完整的应用更新检测流程,包括版本对比、新版本信息展示、下载更新等。本文将深入解析其实现。

二、组件架构

2.1 组件目录结构

check_app_update/
  ├── src/main/ets/
  │   ├── components/
  │   │   └── FindNewVersion.ets    ← 发现新版本页面
  │   ├── model/
  │   │   └── Params.ets            ← 参数模型
  │   └── utils/
  │       ├── AppUpdateUtil.ets     ← 更新工具类
  │       └── DialogController.ets  ← 弹窗控制
  ├── Index.ets                     ← 模块导出
  └── oh-package.json5              ← 模块配置

2.2 更新流程

应用启动 / 手动检查
    │
    ▼
AppUpdateUtil.checkUpdate()
    │
    ├── 请求服务器获取最新版本信息
    │
    ▼
版本对比(versionCode)
    │
    ├── 无新版本 → 不提示
    │
    └── 有新版本 → 展示更新弹窗
        │
        ├── 普通更新
        │   ├── "稍后更新" → 关闭弹窗
        │   └── "立即更新" → 下载安装
        │
        └── 强制更新
            ├── 不可关闭弹窗
            └── 必须更新后才能使用

三、版本信息模型

3.1 数据结构

// Params.ets
export interface UpdateParams {
  versionName: string;    // 新版本名(如 "1.1.0")
  versionCode: number;    // 新版本号(如 1001000)
  updateDesc: string;     // 更新说明
  downloadUrl: string;    // 下载地址
  forceUpdate: boolean;   // 是否强制更新
  packageSize: string;    // 安装包大小(如 "12.5MB")
  publishDate: string;    // 发布日期
}

3.2 版本号对比逻辑

鸿蒙应用的版本号使用 versionCode 进行对比,versionCode 是一个整数,每次发布递增:

// AppScope/app.json5
{
  "versionCode": 1000000,
  "versionName": "1.0.6"
}

推荐的 versionCode 编码规则:

1.0.0 → 1000000
1.0.1 → 1000001
1.0.9 → 1000009
1.1.0 → 1001000
1.2.3 → 1002003
2.0.0 → 2000000

四、更新工具类

4.1 AppUpdateUtil 实现

// AppUpdateUtil.ets
export class AppUpdateUtil {
  private static downloadTask: request.agent.Task | null = null;

  // 检查更新
  static async checkUpdate(): Promise<UpdateParams | null> {
    try {
      // 请求服务器获取最新版本信息
      const response = await https.getUpdateInfo();
      const currentVersion = 1000000; // 当前版本号

      if (response.versionCode > currentVersion) {
        Logger.info('AppUpdateUtil', 'New version found: ' + response.versionName);
        return response;
      }

      Logger.info('AppUpdateUtil', 'Already up to date');
      return null;
    } catch (error) {
      Logger.error('AppUpdateUtil', 'check update failed: ' + error.message);
      return null;
    }
  }

  // 下载更新包
  static async downloadUpdate(downloadUrl: string): Promise<boolean> {
    try {
      // 创建下载任务
      const context = getContext();
      const downloadPath = context.filesDir + '/update.hap';

      // 使用下载代理
      const config: request.agent.Config = {
        url: downloadUrl,
        filePath: downloadPath,
        background: true,
        network: request.agent.Network.WIFI,
      };

      this.downloadTask = await request.agent.create(config);
      await this.downloadTask.start();

      Logger.info('AppUpdateUtil', 'Download completed: ' + downloadPath);
      return true;
    } catch (error) {
      Logger.error('AppUpdateUtil', 'download failed: ' + error.message);
      return false;
    }
  }

  // 安装更新包
  static async installUpdate(filePath: string): Promise<void> {
    try {
      // 调用系统安装器
      const bundleInstaller = getContext().getApplicationContext()
        .getBundleInstaller();
      await bundleInstaller.install(filePath);
    } catch (error) {
      Logger.error('AppUpdateUtil', 'install failed: ' + error.message);
    }
  }

  // 获取下载进度
  static getDownloadProgress(): number {
    if (this.downloadTask) {
      return this.downloadTask.progress;
    }
    return 0;
  }
}

五、更新弹窗组件

5.1 FindNewVersion 组件

// FindNewVersion.ets
@ComponentV2
export struct FindNewVersion {
  @Local updateInfo: UpdateParams | null = null;
  @Local downloadProgress: number = 0;
  @Local isDownloading: boolean = false;

  aboutToAppear() {
    AppUpdateUtil.checkUpdate().then((info) => {
      this.updateInfo = info;
    });
  }

  build() {
    if (this.updateInfo) {
      Column() {
        // 更新图标
        Image($r('app.media.ic_update'))
          .width(64).height(64)
          .margin({ top: 16 });

        // 标题
        Text('发现新版本')
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
          .margin({ top: 12 });

        // 版本号
        Text('v' + this.updateInfo.versionName)
          .fontSize(14)
          .fontColor('#999')
          .margin({ top: 4 });

        // 更新说明
        Scroll() {
          Text(this.updateInfo.updateDesc)
            .fontSize(14)
            .lineHeight(22)
            .padding(16);
        }
        .height(120);

        // 下载进度条
        if (this.isDownloading) {
          Progress({ value: this.downloadProgress, total: 100 })
            .width('100%')
            .color('#64BB5C')
            .margin({ top: 12 });
          Text('下载中 ' + this.downloadProgress + '%')
            .fontSize(12)
            .fontColor('#999');
        }

        // 按钮
        Row({ space: 16 }) {
          if (!this.updateInfo.forceUpdate) {
            // 非强制更新:显示"稍后更新"按钮
            Button('稍后更新')
              .fontColor('#666')
              .backgroundColor('#F5F5F5')
              .borderRadius(20)
              .onClick(() => {
                this.closeDialog();
              });
          }

          Button(this.isDownloading ? '下载中...' : '立即更新')
            .fontColor('#fff')
            .backgroundColor('#64BB5C')
            .borderRadius(20)
            .enabled(!this.isDownloading)
            .onClick(() => {
              this.startUpdate();
            });
        }
        .margin({ top: 16, bottom: 16 });
      }
      .padding(24)
      .width('85%')
      .borderRadius(16)
      .backgroundColor($r('sys.color.comp_background_primary'));
    }
  }

  startUpdate() {
    this.isDownloading = true;
    AppUpdateUtil.downloadUpdate(this.updateInfo.downloadUrl).then((success) => {
      if (success) {
        showToast('下载完成,正在安装...', this.getUIContext());
        // 安装更新
      } else {
        showToast('下载失败,请检查网络', this.getUIContext());
      }
      this.isDownloading = false;
    });
  }
}

5.2 强制更新 vs 普通更新

特性 普通更新 强制更新
弹窗可关闭
显示"稍后更新"
使用场景 功能优化 安全修复、重大 Bug
用户体验 用户可选择 必须更新

六、更新策略

6.1 更新检查时机

// 方案1:应用启动时自动检查
aboutToAppear() {
  AppUpdateUtil.checkUpdate();
}

// 方案2:用户手动检查
Button('检查更新')
  .onClick(() => {
    AppUpdateUtil.checkUpdate().then((info) => {
      if (info) {
        showToast('发现新版本:v' + info.versionName, this.getUIContext());
      } else {
        showToast('已是最新版本', this.getUIContext());
      }
    });
  });

// 方案3:定时检查(后台)
setInterval(() => {
  AppUpdateUtil.checkUpdate();
}, 24 * 60 * 60 * 1000);  // 每天检查一次

6.2 更新说明格式

更新说明建议使用结构化的格式:

功能新增:
  - 新增深色模式支持
  - 新增服务卡片

功能优化:
  - 优化答题页面加载速度
  - 优化字体显示效果

Bug 修复:
  - 修复搜索功能某些关键词查找失败的问题
  - 修复偶现的闪退问题

七、总结

检测应用更新组件实现了完整的更新流程:

  1. 版本检测:通过 versionCode 对比判断是否有新版本
  2. 信息展示:展示新版本号、更新说明、安装包大小
  3. 下载更新:使用下载代理下载安装包
  4. 安装更新:调用系统安装器安装
  5. 强制更新:支持强制更新场景

关键源码文件:

  • components/check_app_update/src/main/ets/components/FindNewVersion.ets — 更新弹窗组件
  • components/check_app_update/src/main/ets/model/Params.ets — 版本信息模型
  • components/check_app_update/src/main/ets/utils/AppUpdateUtil.ets — 更新工具类
  • components/check_app_update/src/main/ets/utils/DialogController.ets — 弹窗控制
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐