HarmonyOS鸿蒙Next应用如何实现应用内更新?
HarmonyOS鸿蒙Next应用如何实现应用内更新? 应用内更新可以让用户在不离开应用的情况下更新版本,本文介绍如何检测新版本、下载安装包、实现静默更新等功能。
开发者您好,对于开发者,应用程序包的更新,首先需要更新app.json5配置文件中的versionCode版本号字段,通过DevEco Studio打包后在应用市场发布,发布流程与首次发布一致。对于终端设备用户,新版本发布后,可以通过以下两种方式更新应用程序包。
- 应用市场内更新:应用市场通知用户该应用有新版本,用户根据通知到应用市场(客户端)进行升级。
- 应用内检测升级:开发者根据更新指导实现版本更新提醒功能,应用启动完成或用户在应用中主动检查新版本时,会弹出升级对话框,用户根据对话框提示升级。
更多关于HarmonyOS鸿蒙Next应用如何实现应用内更新?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
一、应用内更新架构设计
1. 更新流程
检测版本 → 下载安装包 → 验证签名 → 安装更新 → 重启应用
2. 核心功能模块
- 版本检测模块
- 下载管理模块
- 安装管理模块
- UI 提示模块
二、版本检测实现
1. 创建版本检测工具(utils/app-update.js)
/**
* 应用内更新工具
* 支持版本检测、下载、安装
*/
// 版本服务器配置
const UPDATE_CONFIG = {
// 版本检查接口
checkUrl: 'https://your-api.com/api/version/check',
// 下载地址(可选,如果接口返回)
downloadUrl: '',
// 当前版本(从 manifest.json 读取)
currentVersion: '',
// 应用包名
bundleName: 'com.ysy.coms'
}
/**
* 获取当前版本信息
*/
export function getCurrentVersion() {
// #ifdef APP-HARMONY
try {
const manifest = require('@/manifest.json')
return {
versionName: manifest.versionName || '1.0.0',
versionCode: manifest.versionCode || '100',
bundleName: manifest['app-harmony']?.distribute?.bundleName || UPDATE_CONFIG.bundleName
}
} catch (e) {
console.error('获取版本信息失败:', e)
return {
versionName: '1.0.0',
versionCode: '100',
bundleName: UPDATE_CONFIG.bundleName
}
}
// #endif
// #ifndef APP-HARMONY
return {
versionName: '1.0.0',
versionCode: '100',
bundleName: UPDATE_CONFIG.bundleName
}
// #endif
}
/**
* 检查是否有新版本
* @param {Object} options 配置选项
* @returns {Promise<Object>} 版本信息
*/
export async function checkUpdate(options = {}) {
const {
checkUrl = UPDATE_CONFIG.checkUrl,
showLoading = true,
silent = false // 静默检查,不显示提示
} = options
try {
// 显示加载提示
if (showLoading && !silent) {
uni.showLoading({
title: '检查更新中...',
mask: true
})
}
// 获取当前版本
const currentVersion = getCurrentVersion()
// 请求版本信息
const response = await uni.request({
url: checkUrl,
method: 'GET',
data: {
bundleName: currentVersion.bundleName,
currentVersion: currentVersion.versionName,
versionCode: currentVersion.versionCode,
platform: 'harmony'
},
timeout: 10000
})
if (showLoading && !silent) {
uni.hideLoading()
}
if (response.statusCode === 200 && response.data) {
const serverVersion = response.data
// 比较版本
const hasUpdate = compareVersion(
currentVersion.versionName,
serverVersion.versionName
) < 0
if (hasUpdate) {
return {
hasUpdate: true,
currentVersion: currentVersion.versionName,
serverVersion: serverVersion.versionName,
downloadUrl: serverVersion.downloadUrl || serverVersion.apkUrl,
updateLog: serverVersion.updateLog || serverVersion.description || '新版本更新',
forceUpdate: serverVersion.forceUpdate || false, // 是否强制更新
fileSize: serverVersion.fileSize || 0, // 文件大小(字节)
md5: serverVersion.md5 || '' // 文件 MD5 校验
}
} else {
if (!silent) {
uni.showToast({
title: '已是最新版本',
icon: 'none'
})
}
return {
hasUpdate: false,
currentVersion: currentVersion.versionName
}
}
} else {
throw new Error('版本检查失败')
}
} catch (error) {
console.error('检查更新失败:', error)
if (showLoading && !silent) {
uni.hideLoading()
}
if (!silent) {
uni.showToast({
title: '检查更新失败',
icon: 'none'
})
}
return {
hasUpdate: false,
error: error.message
}
}
}
/**
* 比较版本号
* @param {String} version1 版本1
* @param {String} version2 版本2
* @returns {Number} -1: version1 < version2, 0: 相等, 1: version1 > version2
*/
function compareVersion(version1, version2) {
const v1Parts = version1.split('.').map(Number)
const v2Parts = version2.split('.').map(Number)
const maxLength = Math.max(v1Parts.length, v2Parts.length)
for (let i = 0; i < maxLength; i++) {
const v1Part = v1Parts[i] || 0
const v2Part = v2Parts[i] || 0
if (v1Part < v2Part) {
return -1
} else if (v1Part > v2Part) {
return 1
}
}
return 0
}
三、下载管理实现
2. 下载功能实现
/**
* 下载安装包
* @param {String} downloadUrl 下载地址
* @param {Object} options 配置选项
* @returns {Promise<Object>} 下载结果
*/
export async function downloadUpdate(downloadUrl, options = {}) {
const {
onProgress = null, // 进度回调
onSuccess = null, // 成功回调
onError = null // 错误回调
} = options
return new Promise((resolve, reject) => {
// #ifdef APP-HARMONY
try {
// 创建下载任务
const downloadTask = uni.downloadFile({
url: downloadUrl,
success: (res) => {
if (res.statusCode === 200) {
const filePath = res.tempFilePath
if (onSuccess) {
onSuccess(filePath)
}
resolve({
success: true,
filePath: filePath,
statusCode: res.statusCode
})
} else {
const error = new Error(`下载失败: ${res.statusCode}`)
if (onError) {
onError(error)
}
reject(error)
}
},
fail: (error) => {
console.error('下载失败:', error)
if (onError) {
onError(error)
}
reject(error)
}
})
// 监听下载进度
downloadTask.onProgressUpdate((res) => {
const progress = res.progress || 0
if (onProgress) {
onProgress({
progress: progress,
totalBytesWritten: res.totalBytesWritten || 0,
totalBytesExpectedToWrite: res.totalBytesExpectedToWrite || 0
})
}
})
} catch (error) {
console.error('创建下载任务失败:', error)
if (onError) {
onError(error)
}
reject(error)
}
// #endif
// #ifndef APP-HARMONY
reject(new Error('当前环境不支持下载'))
// #endif
})
}
/**
* 验证文件 MD5
* @param {String} filePath 文件路径
* @param {String} expectedMd5 期望的 MD5 值
* @returns {Promise<Boolean>} 是否匹配
*/
export async function verifyFileMd5(filePath, expectedMd5) {
if (!expectedMd5) {
return true // 没有 MD5 值,跳过验证
}
// #ifdef APP-HARMONY
try {
// 使用 plus API 计算文件 MD5
// 注意:需要引入 MD5 计算库或使用原生能力
// 这里简化处理,实际需要实现 MD5 计算
return new Promise((resolve) => {
// 实际实现需要使用 MD5 库
// 例如:使用 crypto-js 或原生 plus API
resolve(true) // 暂时返回 true
})
} catch (error) {
console.error('MD5 验证失败:', error)
return false
}
// #endif
// #ifndef APP-HARMONY
return true
// #endif
}
四、安装管理实现
3. 安装功能实现
/**
* 安装应用
* @param {String} filePath 安装包路径
* @param {Object} options 配置选项
* @returns {Promise<Boolean>} 是否成功
*/
export async function installUpdate(filePath, options = {}) {
const {
onSuccess = null,
onError = null
} = options
return new Promise((resolve, reject) => {
// #ifdef APP-HARMONY
try {
// 使用 plus API 安装应用
plus.runtime.install(
filePath,
{
force: false // 是否强制安装
},
() => {
// 安装成功
console.log('安装成功')
if (onSuccess) {
onSuccess()
}
resolve(true)
},
(error) => {
// 安装失败
console.error('安装失败:', error)
if (onError) {
onError(error)
}
reject(error)
}
)
} catch (error) {
console.error('安装应用失败:', error)
if (onError) {
onError(error)
}
reject(error)
}
// #endif
// #ifndef APP-HARMONY
reject(new Error('当前环境不支持安装'))
// #endif
})
}
/**
* 完整的更新流程
* @param {Object} updateInfo 更新信息
* @param {Object} options 配置选项
* @returns {Promise<Object>} 更新结果
*/
export async function performUpdate(updateInfo, options = {}) {
const {
showProgress = true,
verifyMd5 = true
} = options
try {
// 1. 下载安装包
const downloadResult = await downloadUpdate(updateInfo.downloadUrl, {
onProgress: (progressInfo) => {
if (showProgress) {
const percent = Math.round(progressInfo.progress)
uni.showLoading({
title: `下载中 ${percent}%`,
mask: true
})
}
},
onSuccess: (filePath) => {
console.log('下载成功:', filePath)
},
onError: (error) => {
uni.hideLoading()
uni.showToast({
title: '下载失败',
icon: 'none'
})
}
})
uni.hideLoading()
// 2. 验证文件(可选)
if (verifyMd5 && updateInfo.md5) {
uni.showLoading({
title: '验证文件中...',
mask: true
})
const isValid = await verifyFileMd5(downloadResult.filePath, updateInfo.md5)
uni.hideLoading()
if (!isValid) {
throw new Error('文件校验失败,请重新下载')
}
}
// 3. 安装应用
uni.showLoading({
title: '准备安装...',
mask: true
})
await installUpdate(downloadResult.filePath, {
onSuccess: () => {
uni.hideLoading()
uni.showModal({
title: '安装成功',
content: '应用将在重启后更新',
showCancel: false,
success: () => {
// 重启应用
plus.runtime.restart()
}
})
},
onError: (error) => {
uni.hideLoading()
uni.showModal({
title: '安装失败',
content: error.message || '请手动安装',
showCancel: false
})
}
})
return {
success: true,
filePath: downloadResult.filePath
}
} catch (error) {
console.error('更新失败:', error)
uni.hideLoading()
return {
success: false,
error: error.message
}
}
}
五、UI 组件实现
4. 更新提示组件(components/update-dialog/update-dialog.vue)
<template>
<view class="update-dialog-mask" v-if="visible" @tap="handleMaskTap">
<view class="update-dialog" @tap.stop>
<view class="dialog-header">
<text class="dialog-title">发现新版本</text>
<text class="dialog-version">v{{ updateInfo.serverVersion }}</text>
</view>
<view class="dialog-content">
<view class="update-log">
<text class="log-title">更新内容:</text>
<text class="log-text">{{ updateInfo.updateLog }}</text>
</view>
<view class="update-info" v-if="updateInfo.fileSize">
<text class="info-text">文件大小:{{ formatFileSize(updateInfo.fileSize) }}</text>
</view>
</view>
<view class="dialog-footer">
<button
v-if="!updateInfo.forceUpdate"
class="btn-cancel"
@tap="handleCancel"
>
稍后更新
</button>
<button
class="btn-update"
:class="{ 'btn-full': updateInfo.forceUpdate }"
@tap="handleUpdate"
>
立即更新
</button>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'UpdateDialog',
props: {
visible: {
type: Boolean,
default: false
},
updateInfo: {
type: Object,
default: () => ({})
}
},
methods: {
handleMaskTap() {
// 强制更新时不允许关闭
if (!this.updateInfo.forceUpdate) {
this.$emit('cancel')
}
},
handleCancel() {
this.$emit('cancel')
},
handleUpdate() {
this.$emit('update')
},
formatFileSize(bytes) {
if (!bytes) return '0 B'
const k = 1024
const sizes = ['B', 'KB', 'MB', 'GB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i]
}
}
}
</script>
<style scoped>
.update-dialog-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
}
.update-dialog {
width: 600rpx;
background: #FFFFFF;
border-radius: 24rpx;
overflow: hidden;
}
.dialog-header {
padding: 40rpx 32rpx 24rpx;
text-align: center;
border-bottom: 1rpx solid #E0E0E0;
}
.dialog-title {
display: block;
font-size: 36rpx;
font-weight: 600;
color: #333333;
margin-bottom: 12rpx;
}
.dialog-version {
display: block;
font-size: 28rpx;
color: #666666;
}
.dialog-content {
padding: 32rpx;
max-height: 400rpx;
overflow-y: auto;
}
.update-log {
margin-bottom: 24rpx;
}
.log-title {
display: block;
font-size: 28rpx;
font-weight: 500;
color: #333333;
margin-bottom: 16rpx;
}
.log-text {
display: block;
font-size: 26rpx;
color: #666666;
line-height: 1.6;
white-space: pre-wrap;
}
.update-info {
padding-top: 24rpx;
border-top: 1rpx solid #F0F0F0;
}
.info-text {
font-size: 24rpx;
color: #999999;
}
.dialog-footer {
display: flex;
padding: 24rpx 32rpx 32rpx;
gap: 24rpx;
}
.btn-cancel,
.btn-update {
flex: 1;
height: 88rpx;
line-height: 88rpx;
text-align: center;
border-radius: 44rpx;
font-size: 32rpx;
border: none;
}
.btn-cancel {
background: #F5F5F5;
color: #666666;
}
.btn-update {
background: linear-gradient(135deg, #6BBF59 0%, #AED581 100%);
color: #FFFFFF;
}
.btn-full {
flex: 1;
width: 100%;
}
</style>
六、集成到项目
5. 在 App.vue 中集成
// App.vue
import { checkUpdate, performUpdate } from '@/utils/app-update.js'
import UpdateDialog from '@/components/update-dialog/update-dialog.vue'
export default {
components: {
UpdateDialog
},
data() {
return {
updateInfo: null,
showUpdateDialog: false,
isUpdating: false
}
},
onLaunch: async function() {
// ... 其他初始化代码
// 延迟检查更新(不阻塞启动)
setTimeout(() => {
this.checkAppUpdate(true) // 静默检查
}, 3000)
},
methods: {
/**
* 检查应用更新
* @param {Boolean} silent 是否静默检查
*/
async checkApp鸿蒙Next应用内更新可通过AppGallery Connect的版本发布功能实现。开发者需集成AGC的App Update SDK,调用checkAppUpdate接口检测新版本,支持静默安装和用户确认两种更新方式。静默更新需在config.json中配置allowAppUpdatePermission权限。
在HarmonyOS Next中实现应用内更新,核心是使用@ohos.update模块。以下是关键步骤和代码示例:
1. 检测新版本
通过UpdateService检查是否有可用更新:
import update from '@ohos.update';
let updateService = update.getUpdateService();
updateService.checkNewVersion((err, data) => {
if (!err && data.hasNewVersion) {
// 有新版本,获取版本信息
let versionInfo = data.newVersionInfo;
console.log(`新版本:${versionInfo.versionName}`);
}
});
2. 下载安装包
确认更新后,开始下载:
updateService.download((err, data) => {
if (err) {
console.error('下载失败', err);
return;
}
// 监听下载进度
console.log(`下载进度:${data.receivedSize}/${data.totalSize}`);
}, {
allowNetwork: update.NetType.NET_MOBILE // 允许使用的网络类型
});
3. 安装更新包
下载完成后自动触发安装,或手动调用:
// 自动安装(需用户授权)
updateService.install();
// 或使用静默安装(需系统权限)
updateService.install({
mode: update.InstallMode.SILENT
});
4. 静默更新实现
对于系统应用或有权限的应用,可配置静默更新:
// 在config.json中声明权限
"reqPermissions": [
{
"name": "ohos.permission.INSTALL_BUNDLE"
}
]
// 静默下载并安装
updateService.downloadAndInstall({
installMode: update.InstallMode.SILENT,
allowNetwork: update.NetType.NET_WIFI // 仅WiFi下静默更新
});
5. 更新策略配置
可设置更灵活的更新策略:
updateService.setPolicy({
autoDownload: true, // 有更新时自动下载
installMode: update.InstallMode.USER_INITIATED, // 用户确认后安装
allowedNetworks: [update.NetType.NET_WIFI] // 限制网络类型
});
注意事项:
- 非系统应用通常需要用户确认才能安装
- 静默安装需要
INSTALL_BUNDLE系统权限 - 建议在WiFi环境下进行大版本更新
- 需处理更新失败的回退机制
通过以上API组合,可实现从检测到安装的完整应用内更新流程。根据应用类型和权限不同,可选择用户交互更新或静默更新方案。

