应用上线之后,版本迭代是常态。新版本修了bug、加了功能,怎么推给用户?自己做一套更新系统,要搭服务器、存安装包、写下载逻辑、处理断点续传,工作量不小。鸿蒙内置了版本升级模块,从检测更新、下载安装到升级策略,全套能力都提供了,不用自己从头造轮子。

本文基于最新鸿蒙API,把版本升级的完整流程讲清楚:怎么检测新版本、下载进度怎么监听、安装怎么适配,代码可以直接复制到项目里跑。

01 版本升级能力概览

鸿蒙的版本升级能力由@kit.UpgradeKit提供,核心是UpdateClient客户端。它帮你处理了几件事:

  • 更新检测:查询当前应用有没有可用的新版本

  • 下载安装包:后台下载APK/HAP,支持断点续传

  • 状态监听:下载进度、安装状态、错误回调

  • 升级策略:强制更新、可选更新、静默升级

底层依赖华为应用市场的升级通道,安装包托管在华为服务器上,不用自己搭文件服务。开发者只需要在AppGallery Connect后台上传新版本,代码里调用接口就能检测和下载。

使用前需要先在module.json5里配置权限:

"requestPermissions": [
  {
    "name": "ohos.permission.INTERNET",
    "reason": "$string:net_reason",
    "usedScene": {
      "abilities": ["EntryAbility"],
      "when": "inuse"
    }
  }
]

网络权限是必须的,检测更新和下载安装包都要联网。

02 更新检测实现

检测更新的核心是调用checkUpdate方法,传入上下文,系统会自动对比当前版本和应用市场上的最新版本。

import { update } from '@kit.UpgradeKit';
import { common } from '@kit.AbilityKit';

async function checkForUpdate(context: common.Context) {
  // 创建升级客户端
  const updateClient = update.createUpdateClient(context);

  // 注册检查结果回调
  updateClient.on('eventInfo', (result: update.UpdateResult) => {
    console.info('升级检查结果:', result.installState);
    // installState: 0=无更新, 1=有新版本
    if (result.installState === 1) {
      console.info('发现新版本,版本号:', result.versionName);
      console.info('版本大小:', result.versionSize);
      console.info('更新说明:', result.releaseNotes);
    } else {
      console.info('已经是最新版本');
    }
  });

  // 发起检测
  await updateClient.checkUpdate();
}

检测结果里几个关键字段:

  • installState:0代表没有更新,1代表有新版本

  • versionName:新版本的版本名,比如2.1.0

  • versionSize:安装包大小,单位字节

  • releaseNotes:更新说明,就是后台填的更新日志

检测时机一般选在应用启动后、设置页的"关于我们"里。不要一启动就弹更新提示,用户刚打开APP就被打断体验不好。可以在首页加载完之后静默检测,有更新了再弹提示。

还有一个细节:检测更新是异步的,不要在启动页等检测结果返回再往下走。先让用户进首页,检测完了再弹更新弹窗。

03 下载进度与状态监听

用户确认要更新之后,就开始下载安装包。下载过程中要监听进度,给用户展示下载百分比,不然用户不知道下到哪了,会以为卡了。

import { update } from '@kit.UpgradeKit';
import { promptAction } from '@kit.ArkUI';

@Entry
@Component
struct UpdatePage {
  @State downloadProgress: number = 0;
  @State isDownloading: boolean = false;
  private updateClient: update.UpdateClient | null = null;

  // 开始下载
  async startDownload(context: common.Context) {
    this.updateClient = update.createUpdateClient(context);
    this.isDownloading = true;

    // 监听下载进度
    this.updateClient.on('downloadProgress', (progress: number) => {
      this.downloadProgress = progress;
      console.info('下载进度:', progress, '%');
    });

    // 监听下载状态变化
    this.updateClient.on('installStatus', (status: update.InstallStatus) => {
      console.info('安装状态:', status);
      // status: 0=下载中, 1=下载完成, 2=安装中, 3=安装成功, 4=失败
      if (status === update.InstallStatus.INSTALL_SUCCESS) {
        this.isDownloading = false;
        promptAction.showToast({ message: '更新安装完成' });
      } else if (status === update.InstallStatus.INSTALL_FAILURE) {
        this.isDownloading = false;
        promptAction.showToast({ message: '更新失败,请重试' });
      }
    });

    // 发起下载
    await this.updateClient.startDownload();
  }

  build() {
    Column() {
      Text('版本更新')
        .fontSize(20)
        .margin({ bottom: 20 })

      if (this.isDownloading) {
        Text(`下载中... ${this.downloadProgress}%`)
          .fontSize(16)
          .margin({ bottom: 10 })
        Progress({ value: this.downloadProgress, total: 100, type: ProgressType.Linear })
          .width('80%')
      } else {
        Button('立即更新')
          .width('60%')
          .onClick(() => {
            this.startDownload(getContext(this));
          })
      }
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

下载进度是百分比数值,0到100,直接绑定到进度条组件上就行。系统会自动处理断点续传,下载中途网络断了,下次打开会接着下,不用自己实现。

还有一个注意点:下载大安装包的时候,建议提示用户在WiFi环境下下载,不然流量费用户会投诉。可以判断当前网络类型,如果是蜂窝网络,先弹个提示确认再开始下载。

04 安装与升级策略

下载完成之后,系统会自动弹出安装界面,用户确认后开始安装。安装过程中应用会退出,安装完成后自动打开新版本。

升级策略有两种,按业务需要选:

可选更新:用户可以选择"稍后再说",不强制升级。大部分普通版本用这种,用户体验好。

强制更新:老版本有严重bug,不升级不能用。这时候用户不更新就退出应用,不能跳过。

async function checkUpdateAndPrompt(context: common.Context) {
  const client = update.createUpdateClient(context);

  client.on('eventInfo', (result: update.UpdateResult) => {
    if (result.installState === 1) {
      // 判断是否强制更新,这个标识从服务器配置或者版本标记里来
      const isForceUpdate = result.versionCode > 100; // 示例:高于某版本号强制

      if (isForceUpdate) {
        // 强制更新:弹不可关闭的弹窗
        showForceUpdateDialog(context, result);
      } else {
        // 可选更新:弹可以关闭的弹窗
        showOptionalUpdateDialog(context, result);
      }
    }
  });

  await client.checkUpdate();
}

// 强制更新弹窗
function showForceUpdateDialog(context: common.Context, info: update.UpdateResult) {
  // 自定义弹窗,不允许点击外部关闭
  // 内容:有新版本必须更新才能继续使用
  // 按钮:只有"立即更新",没有"稍后"
  console.info('强制更新弹窗');
}

// 可选更新弹窗
function showOptionalUpdateDialog(context: common.Context, info: update.UpdateResult) {
  // 自定义弹窗,可以关闭
  // 按钮:"立即更新" 和 "稍后再说"
  console.info('可选更新弹窗');
}

强制更新的标识不是升级接口给的,是自己定义的。一般做法是后端返回一个最低支持版本号,当前版本低于这个号就强制更新。或者在AppGallery后台上传版本的时候,标记这个版本是强制升级。

05 完整升级流程封装

把检测、下载、弹窗、策略都封装到一个工具类里,项目里直接调用。

import { update } from '@kit.UpgradeKit';
import { common } from '@kit.AbilityKit';
import { promptAction } from '@kit.ArkUI';

class UpdateManager {
  private static instance: UpdateManager;
  private updateClient: update.UpdateClient | null = null;

  static getInstance(): UpdateManager {
    if (!UpdateManager.instance) {
      UpdateManager.instance = new UpdateManager();
    }
    return UpdateManager.instance;
  }

  // 检查更新,自动弹提示
  async checkAndPrompt(context: common.Context) {
    this.updateClient = update.createUpdateClient(context);

    this.updateClient.on('eventInfo', async (result) => {
      if (result.installState !== 1) {
        promptAction.showToast({ message: '当前已是最新版本' });
        return;
      }

      // 这里可以根据result判断是强制还是可选
      const needForce = result.versionCode >= 200;
      if (needForce) {
        await this.forceUpdateFlow(result);
      } else {
        await this.optionalUpdateFlow(result);
      }
    });

    try {
      await this.updateClient.checkUpdate();
    } catch (e) {
      console.error('检查更新失败:', e);
    }
  }

  // 可选更新流程
  private async optionalUpdateFlow(result: update.UpdateResult) {
    // 弹确认框,用户点确认再下载
    console.info('可选更新,用户确认后下载');
    // 实际项目里用AlertDialog实现
  }

  // 强制更新流程
  private async forceUpdateFlow(result: update.UpdateResult) {
    // 弹不可关闭的弹窗
    console.info('强制更新,必须下载安装');
  }

  // 开始下载
  async startDownload() {
    if (!this.updateClient) return;
    this.updateClient.on('downloadProgress', (progress) => {
      console.info('下载进度:', progress);
    });
    this.updateClient.on('installStatus', (status) => {
      if (status === update.InstallStatus.INSTALL_SUCCESS) {
        promptAction.showToast({ message: '更新完成' });
      }
    });
    await this.updateClient.startDownload();
  }
}

// 使用方式:在EntryAbility的onCreate里调用
// UpdateManager.getInstance().checkAndPrompt(context);

这样封装之后,页面里只需要调一个checkAndPrompt方法,剩下的检测、弹窗、下载逻辑都在内部处理。

落地注意事项

**检测时机要合理。**不要一打开APP就弹更新提示,用户刚进来就被打断体验不好。建议在首页加载完成、用户操作了几秒之后静默检测,有更新再弹。

**大安装包提示WiFi。**安装包超过100M的话,蜂窝网络下下载会耗流量。开始下载前判断一下当前网络,如果是流量,弹个提示问用户要不要继续。

**更新日志要写清楚。**弹更新弹窗的时候,把版本更新说明列出来,用户知道更新了什么才愿意更。不要只写"新版本上线",太敷衍了。

**失败要有重试。**下载失败了不能直接结束,要给用户重试的机会。网络波动很正常,失败点个按钮重新下就行。

小结

鸿蒙内置的版本升级模块,把检测、下载、安装这套流程都封装好了,不用自己搭服务、写断点续传。开发者只需要在AppGallery后台上传新版本,代码里调几个接口就能完成完整的升级流程。

实际项目中,把升级逻辑封装到统一的工具类,检测时机选好,强制和可选策略分清楚,下载进度给用户看明白。用户升级体验好了,新版本的覆盖率自然就上去了。

Logo

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

更多推荐