HarmonyOS开发—— uni-app 鸿蒙端(HarmonyOS NEXT)整包更新适配:告别热更新,拥抱应用市场
前言
在 uni-app 跨端开发中,Android/iOS 端的热更新(WGT)已经是非常成熟的方案——通过 plus.runtime.install() 下载差量包静默安装,用户无感知即可完成版本迭代。然而,当我们把目光转向 HarmonyOS NEXT 时,问题出现了:
鸿蒙端不支持 WGT 热更新。
这不是 bug,而是平台设计使然。HarmonyOS NEXT 的应用分发体系要求通过华为应用市场(AppGallery)进行整包更新。本文将分享我们在实际项目中的适配方案:通过原生桥接调用鸿蒙 updateManager API,由官方弹窗引导用户前往应用市场更新,同时确保 Android/iOS 的热更新流程零改动。
一、问题背景
我们的项目是一个基于 uni-app + Vue3 的多端应用,同时覆盖 Android、iOS 和 HarmonyOS NEXT 三端。原有的更新体系如下:
| 平台 | 更新方式 | 核心 API |
|---|---|---|
| Android | WGT 热更新 / APK 强制更新 | plus.runtime.install() |
| iOS | WGT 热更新 / App Store 跳转 | plus.runtime.install() |
| HarmonyOS NEXT | ❓ | ❓ |
Android/iOS 的更新流程由后端配置中心下发 HOT_UPDATE_URL,客户端读取后根据版本号比较,自动判断是热更新(WGT)、强制更新(APK)还是应用市场跳转。
鸿蒙端面临的核心矛盾:
plus.runtime在鸿蒙端不可用,无法获取 WGT 版本信息- 鸿蒙不支持 WGT 差量包安装
- 必须走华为应用市场的整包更新通道
二、方案设计
2.1 设计原则
- 平台隔离:鸿蒙端的更新逻辑完全独立,不干扰 Android/iOS 已有流程
- 原生能力优先:版本检测和更新弹窗均由鸿蒙原生 API 完成,前端只负责触发
- 零侵入:Android/iOS 的更新代码不做任何改动
2.2 整体架构
┌─────────────────────────────────────────────────┐
│ App.vue (onLaunch) │
│ checkBundleUpdate() │
├─────────────────────────────────────────────────┤
│ │
│ #ifdef APP-HARMONY #ifndef APP-HARMONY│
│ ┌─────────────────┐ ┌──────────────────┐│
│ │ 鸿蒙应用市场更新 │ │ 读取后端配置 ││
│ │ checkHarmony- │ │ HOT_UPDATE_URL ││
│ │ MarketUpdate() │ │ 判断 WGT/APK/ ││
│ │ ↓ │ │ Market 更新类型 ││
│ │ 原生桥接调用 │ │ 执行对应更新流程 ││
│ │ uni.checkHarmony│ └──────────────────┘│
│ │ AppUpdate() │ │
│ └────────┬────────┘ │
└────────────┼─────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ EntryAbility.ets (原生层) │
│ │
│ updateManager.checkAppUpdate() → 检测新版本 │
│ updateManager.showUpdateDialog() → 官方弹窗引导 │
└─────────────────────────────────────────────────┘
三、实现细节
3.1 原生桥接层(ArkTS)
在鸿蒙原生工程 EntryAbility.ets 中,通过 updateManager API 实现版本检测和官方弹窗:
import { updateManager } from '@kit.AppStoreServiceKit';
// 在 onCreate 中注册桥接方法
Reflect.set(uni, 'checkHarmonyAppUpdate', () => this.checkHarmonyAppUpdate());
/** 仅检查鸿蒙应用市场的整包版本,由官方弹窗引导更新。 */
private async checkHarmonyAppUpdate(): Promise<string> {
if (this.appUpdateChecking) {
return 'checking';
}
this.appUpdateChecking = true;
try {
// 第一步:检查是否有新版本
const result = await updateManager.checkAppUpdate(this.context);
if (result.updateAvailable === updateManager.UpdateAvailableCode.LATER_VERSION_NOT_EXIST) {
return 'not_update'; // 已是最新版本
}
if (result.updateAvailable !== updateManager.UpdateAvailableCode.LATER_VERSION_EXIST) {
throw new Error('未知的应用市场更新检测结果');
}
// 第二步:有新版,展示官方更新弹窗
const dialogResult = await updateManager.showUpdateDialog(this.context);
if (dialogResult === updateManager.ShowUpdateResultCode.SHOW_DIALOG_SUCCESS) {
return 'dialog_shown'; // 弹窗已展示
}
return 'dialog_failed'; // 弹窗展示失败
} catch (err) {
const error = err as BusinessError;
throw new Error(`鸿蒙应用市场更新检查失败:${error.code} ${error.message}`);
} finally {
this.appUpdateChecking = false;
}
}
关键点:
- 使用
@kit.AppStoreServiceKit的updateManager,这是华为官方提供的应用市场服务接口 checkAppUpdate()检测应用市场是否有新版本showUpdateDialog()弹出华为官方的更新确认弹窗,用户点击后自动跳转应用市场完成更新- 通过
Reflect.set(uni, ...)将方法挂载到uni对象,供前端 JS 层调用
3.2 前端调用层(TypeScript)
在 useUpdateInfo.ts 中,通过条件编译隔离鸿蒙的更新逻辑:
// 鸿蒙端专用:调用原生桥接,触发应用市场更新检查
// #ifdef APP-HARMONY
async function checkHarmonyMarketUpdate(isShowNotice: boolean) {
const harmonyApi = uni as typeof uni & {
checkHarmonyAppUpdate?: () => Promise<string>;
};
if (typeof harmonyApi.checkHarmonyAppUpdate !== 'function') {
throw new Error('鸿蒙应用市场更新桥接未就绪,需重新编译原生工程');
}
const result = await harmonyApi.checkHarmonyAppUpdate();
if (!isShowNotice) return;
// 根据原生层返回的结果,给出对应的用户提示
if (result === 'not_update') {
uni.showToast({ title: '当前已是最新版本', icon: 'none' });
} else if (result === 'dialog_failed') {
uni.showToast({ title: '发现新版本,请前往华为应用市场更新', icon: 'none' });
} else if (result === 'checking') {
uni.showToast({ title: '正在检查更新,请稍候', icon: 'none' });
}
// result === 'dialog_shown' 时,官方弹窗已展示,无需额外提示
}
// #endif
3.3 更新入口:条件编译分流
在 checkBundleUpdate 主函数中,通过 #ifdef APP-HARMONY 实现平台分流:
async function checkBundleUpdate(isShowNotice = false) {
if (isDevEnv()) return;
try {
const widgetInfo = await getCurrentWidgetInfo();
// #ifdef APP-HARMONY
// 鸿蒙整包更新由原生应用市场判断并展示官方弹窗,不使用热更新弹窗。
isShowPopup.value = false;
await checkHarmonyMarketUpdate(isShowNotice);
return; // ← 直接返回,不走 Android 的逻辑
// #endif
// #ifndef APP-HARMONY
// Android/iOS:从后端读取热更新配置
const res = await getConfigKey('HOT_UPDATE_URL');
const resp = res?.configValue;
// ... 版本号比较、WGT 下载、APK 安装等原有逻辑
// #endif
} catch (e) {
console.log('error>>', e);
}
}
3.4 版本号获取的平台差异
鸿蒙端 plus.runtime 不可用,需要用 uni.getAppBaseInfo() 替代:
function getCurrentWidgetInfo(): Promise<{ version: string }> {
return new Promise((resolve) => {
// #ifdef APP-HARMONY
let version = '0.0.0';
try {
const appBaseInfo: any = uni.getAppBaseInfo();
version = appBaseInfo?.appVersion || '0.0.0';
} catch (err) {
console.log('getAppBaseInfo error', err);
}
resolve({ version });
return;
// #endif
// #ifndef APP-HARMONY
plus.runtime.getProperty(plus.runtime.appid, (widgetInfo) => {
resolve(widgetInfo as { version: string });
});
// #endif
});
}
四、踩坑记录
4.1 plus.runtime 在鸿蒙端不可用
这是最常见的坑。Android/iOS 上习以为常的 plus.runtime.getProperty()、plus.runtime.install() 在鸿蒙端全部不可用。不仅不能用来安装 WGT,连获取当前版本号都做不到。
解决方案:使用 uni.getAppBaseInfo().appVersion 获取版本号。
4.2 条件编译的边界陷阱
uni-app 的条件编译中,APP-PLUS 仅覆盖 Android/iOS,而 APP 包含所有 App 端(含鸿蒙)。因此:
// 这段代码在鸿蒙端也会执行!
// #ifdef APP-PLUS || APP-HARMONY
// ...
// #endif
// 如果只想在 Android/iOS 执行:
// #ifdef APP-PLUS
// ...
// #endif
// 如果只想在鸿蒙执行:
// #ifdef APP-HARMONY
// ...
// #endif
务必确认每个条件编译块的执行范围,避免鸿蒙端误执行 Android 的热更新逻辑。
4.3 原生桥接的注册时机
Reflect.set(uni, 'checkHarmonyAppUpdate', ...) 必须在 EntryAbility 的 onCreate 中完成,确保前端调用时桥接方法已就绪。如果注册过晚,前端会抛出"桥接未就绪"的错误。
4.4 防重入保护
用户可能快速多次触发更新检查,必须加防重入锁:
private appUpdateChecking = false;
// 检查中直接返回,不重复调用原生 API
if (this.appUpdateChecking) {
return 'checking';
}
this.appUpdateChecking = true;
try {
// ... 检查逻辑
} finally {
this.appUpdateChecking = false; // 无论成功失败都释放锁
}
五、三端更新策略对比
| 维度 | Android | iOS | HarmonyOS NEXT |
|---|---|---|---|
| 更新方式 | WGT 热更新 / APK 强制安装 | WGT 热更新 / App Store | 华为应用市场整包更新 |
| 版本检测 | 后端配置中心 HOT_UPDATE_URL |
同 Android | 原生 updateManager.checkAppUpdate() |
| 更新弹窗 | 自定义前端弹窗 | 自定义前端弹窗 | 华为官方弹窗 |
| 用户感知 | 静默下载,弹窗提示安装 | 静默下载,弹窗提示安装 | 官方弹窗引导跳转应用市场 |
| 核心 API | plus.runtime.install() |
plus.runtime.install() |
updateManager.showUpdateDialog() |
| 前端参与度 | 高(下载、安装、进度条) | 高 | 低(仅触发,UI 由官方接管) |
六、总结
鸿蒙 NEXT 的应用生态与 Android/iOS 有本质区别——它不允许应用内热更新,而是通过华为应用市场统一管理版本分发。对于 uni-app 开发者来说,适配的关键在于:
- 放弃热更新思维:不要试图在鸿蒙端复刻 Android 的 WGT 方案
- 善用原生能力:
@kit.AppStoreServiceKit提供了完整的版本检测和更新弹窗能力 - 条件编译隔离:
#ifdef APP-HARMONY是平台隔离的核心武器 - 桥接设计要简洁:前端只负责"触发",具体的检测、弹窗、跳转全部交给原生层
这种"前端轻、原生重"的设计模式,在鸿蒙生态日趋完善的背景下,会越来越成为跨端开发的常态。
浙公网安备 33010602011771号