HarmonyOS开发:应用内更新InAppUpdate
HarmonyOS开发:应用内更新InAppUpdate
📌 核心要点:应用内更新让用户不用离开应用就能完成版本升级——检测更新、下载安装包、展示进度、触发安装,一气呵成。但HarmonyOS的安全机制决定了,安装环节必须用户手动确认,你不能静默升级。
背景与动机
传统的更新流程是这样的:应用检测到新版本→弹窗提示→跳转到AppGallery→用户在AppGallery里下载安装→切回应用。
这个流程有什么问题?用户流失。每多一步跳转,就有一部分用户放弃更新。尤其是那些网络不太好的用户,跳到AppGallery发现要下载100多MB,直接关了。
应用内更新(InAppUpdate)解决了这个问题。用户不用离开你的应用,在应用内就能完成下载,下载完一键安装。流程更短,转化率更高。
但要注意:HarmonyOS的安全机制不允许应用静默安装。下载完了,还得弹一个系统级的安装确认弹窗,用户点了"安装"才会真正更新。这是安全底线,不能绕过。
这篇文章,把InAppUpdate的API使用、下载管理、进度展示、安装触发全部讲清楚。
核心原理
InAppUpdate完整流程
graph TD
A[应用启动/手动检查] --> B[调用InAppUpdate API检测]
B --> C{有新版本?}
C -->|否| D[正常使用]
C -->|是| E[获取更新信息]
E --> F[展示更新弹窗]
F --> G{用户选择}
G -->|拒绝| D
G -->|同意更新| H[创建下载任务]
H --> I[下载安装包]
I --> J[展示下载进度]
J --> K{下载完成?}
K -->|失败| L[展示错误提示]
L --> M{重试?}
M -->|是| H
M -->|否| D
K -->|成功| N[触发安装]
N --> O[系统安装确认弹窗]
O --> P{用户确认?}
P -->|是| Q[安装并重启]
P -->|否| D
classDef startStyle fill:#FF6B35,stroke:#D4551F,color:#fff,font-weight:bold
classDef processStyle fill:#4ECDC4,stroke:#3BA99C,color:#fff
classDef decisionStyle fill:#FFE66D,stroke:#D4B93C,color:#333,font-weight:bold
classDef dangerStyle fill:#FF6B6B,stroke:#CC5555,color:#fff
classDef successStyle fill:#96CEB4,stroke:#6DAF8E,color:#fff,font-weight:bold
class A,B startStyle
class D,E,F,H,I,J,N processStyle
class C,G,K,M,O,P decisionStyle
class L dangerStyle
class Q successStyle
InAppUpdate vs 传统更新
| 维度 | InAppUpdate | 传统跳转AppGallery |
|---|---|---|
| 用户离开应用 | 不需要 | 需要 |
| 下载进度展示 | 应用内自定义 | AppGallery标准UI |
| 下载中断恢复 | 可自定义 | AppGallery处理 |
| 安装确认 | 系统弹窗 | AppGallery处理 |
| 审核要求 | 需要说明更新机制 | 无特殊要求 |
| 适用场景 | 大型应用、高频更新 | 小型应用、低频更新 |
代码实战
基础用法:检测更新
// entry/src/main/ets/utils/InAppUpdateManager.ets
// 应用内更新管理器——检测、下载、安装一站式管理
import { hilog } from '@kit.PerformanceAnalysisKit';
import { common } from '@kit.AbilityKit';
import { bundleManager } from '@kit.AbilityKit';
// 更新信息
interface AppUpdateInfo {
versionCode: number; // 新版本号
versionName: string; // 新版本名
updateLog: string; // 更新日志
packageSize: number; // 安装包大小(字节)
downloadUrl: string; // 下载地址
md5: string; // 安装包MD5校验
isForceUpdate: boolean; // 是否强制更新
minSupportVersionCode: number; // 最低支持版本
}
// 更新状态
enum UpdateStatus {
IDLE = 'idle', // 空闲
CHECKING = 'checking', // 检查中
AVAILABLE = 'available', // 有更新可用
DOWNLOADING = 'downloading', // 下载中
DOWNLOADED = 'downloaded', // 已下载
INSTALLING = 'installing', // 安装中
FAILED = 'failed', // 失败
CANCELED = 'canceled' // 已取消
}
export class InAppUpdateManager {
private static instance: InAppUpdateManager;
private context: common.Context | null = null;
private updateInfo: AppUpdateInfo | null = null;
private status: UpdateStatus = UpdateStatus.IDLE;
private onStatusChange?: (status: UpdateStatus) => void;
static getInstance(): InAppUpdateManager {
if (!InAppUpdateManager.instance) {
InAppUpdateManager.instance = new InAppUpdateManager();
}
return InAppUpdateManager.instance;
}
// 初始化
async init(context: common.Context): Promise<void> {
this.context = context;
hilog.info(0x0000, 'InAppUpdate', '应用内更新管理器初始化完成');
}
// 检查更新
async checkForUpdate(): Promise<{
hasUpdate: boolean;
updateInfo: AppUpdateInfo | null;
}> {
this.setStatus(UpdateStatus.CHECKING);
try {
// 获取当前版本
const bundleInfo = await bundleManager.getBundleInfoForSelf(
bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT
);
const currentVersionCode = bundleInfo.versionCode;
// 从服务端获取更新信息
const serverInfo = await this.fetchUpdateInfo();
if (!serverInfo || serverInfo.versionCode <= currentVersionCode) {
this.setStatus(UpdateStatus.IDLE);
return { hasUpdate: false, updateInfo: null };
}
this.updateInfo = serverInfo;
this.setStatus(UpdateStatus.AVAILABLE);
hilog.info(0x0000, 'InAppUpdate',
`发现新版本: ${serverInfo.versionName}(${serverInfo.versionCode})`);
return { hasUpdate: true, updateInfo: serverInfo };
} catch (error) {
hilog.error(0x0000, 'InAppUpdate', `检查更新失败: ${JSON.stringify(error)}`);
this.setStatus(UpdateStatus.FAILED);
return { hasUpdate: false, updateInfo: null };
}
}
// 获取更新信息
getUpdateInfo(): AppUpdateInfo | null {
return this.updateInfo;
}
// 获取当前状态
getStatus(): UpdateStatus {
return this.status;
}
// 设置状态变更回调
setOnStatusChange(callback: (status: UpdateStatus) => void): void {
this.onStatusChange = callback;
}
// 从服务端获取更新信息(模拟)
private async fetchUpdateInfo(): Promise<AppUpdateInfo | null> {
// 实际项目中通过HTTP请求获取
return {
versionCode: 10500,
versionName: '1.5.0',
updateLog: '1. 修复了数据同步异常的问题\n2. 优化了页面加载速度\n3. 新增了深色模式支持',
packageSize: 85 * 1024 * 1024, // 85MB
downloadUrl: 'https://appgallery.huawei.com/download/com.example.myapp',
md5: 'a1b2c3d4e5f6g7h8i9j0',
isForceUpdate: false,
minSupportVersionCode: 10300
};
}
// 更新状态
private setStatus(status: UpdateStatus): void {
const oldStatus = this.status;
this.status = status;
if (oldStatus !== status) {
hilog.info(0x0000, 'InAppUpdate', `状态变更: ${oldStatus} → ${status}`);
this.onStatusChange?.(status);
}
}
}
进阶用法:下载管理与进度展示
下载是InAppUpdate的核心环节。大文件下载需要考虑断点续传、网络切换、进度展示等问题。
// entry/src/main/ets/utils/UpdateDownloader.ets
// 更新下载器——支持断点续传、进度回调、网络切换处理
import { hilog } from '@kit.PerformanceAnalysisKit';
import { common } from '@kit.AbilityKit';
import { request } from '@kit.BasicServicesKit';
import { fileIo as fs } from '@kit.CoreFileKit';
// 下载进度信息
interface DownloadProgress {
downloadedSize: number; // 已下载大小(字节)
totalSize: number; // 总大小(字节)
progress: number; // 进度百分比(0-100)
speed: number; // 下载速度(字节/秒)
remainingTime: number; // 预计剩余时间(秒)
}
// 下载配置
interface DownloadConfig {
url: string; // 下载地址
fileName: string; // 保存文件名
header?: Record<string, string>; // 自定义请求头
enableMetered: boolean; // 是否允许计费网络下载
enableRoaming: boolean; // 是否允许漫游下载
}
export class UpdateDownloader {
private context: common.Context | null = null;
private downloadTask: request.DownloadTask | null = null;
private isDownloading: boolean = false;
private startTime: number = 0;
private lastProgressTime: number = 0;
private lastDownloadedSize: number = 0;
// 回调
onProgress?: (progress: DownloadProgress) => void;
onComplete?: (filePath: string) => void;
onError?: (error: string) => void;
// 初始化
init(context: common.Context): void {
this.context = context;
}
// 开始下载
async startDownload(config: DownloadConfig): Promise<void> {
if (this.isDownloading) {
hilog.warn(0x0000, 'Downloader', '已有下载任务在运行');
return;
}
if (!this.context) {
this.onError?.('上下文未初始化');
return;
}
try {
// 构建下载保存路径
const filesDir = this.context.filesDir;
const filePath = `${filesDir}/update/${config.fileName}`;
// 确保目录存在
const dirPath = `${filesDir}/update`;
if (!fs.accessSync(dirPath)) {
fs.mkdirSync(dirPath);
}
// 如果文件已存在,删除旧文件
if (fs.accessSync(filePath)) {
fs.unlinkSync(filePath);
}
// 创建下载任务
const downloadConfig: request.DownloadConfig = {
url: config.url,
filePath: filePath,
header: config.header || {},
enableMetered: config.enableMetered,
enableRoaming: config.enableRoaming,
description: '应用更新包',
networkType: config.enableMetered ? 1 : 0, // 0=WiFi, 1=任何网络
title: '应用更新'
};
this.downloadTask = await request.downloadFile(this.context, downloadConfig);
this.isDownloading = true;
this.startTime = Date.now();
this.lastProgressTime = Date.now();
hilog.info(0x0000, 'Downloader', `下载任务已创建: ${config.url}`);
// 监听下载进度
this.downloadTask.on('progress', (receivedSize: number, totalSize: number) => {
this.handleProgress(receivedSize, totalSize);
});
// 监听下载完成
this.downloadTask.on('complete', () => {
this.handleComplete(filePath);
});
// 监听下载失败
this.downloadTask.on('fail', (err: number) => {
this.handleError(`下载失败,错误码: ${err}`);
});
} catch (error) {
this.handleError(`创建下载任务失败: ${JSON.stringify(error)}`);
}
}
// 暂停下载
async pauseDownload(): Promise<void> {
if (!this.downloadTask || !this.isDownloading) return;
try {
// HarmonyOS的downloadTask支持suspend
await this.downloadTask.suspend();
this.isDownloading = false;
hilog.info(0x0000, 'Downloader', '下载已暂停');
} catch (error) {
hilog.error(0x0000, 'Downloader', `暂停下载失败: ${JSON.stringify(error)}`);
}
}
// 恢复下载
async resumeDownload(): Promise<void> {
if (!this.downloadTask || this.isDownloading) return;
try {
await this.downloadTask.resume();
this.isDownloading = true;
this.lastProgressTime = Date.now();
hilog.info(0x0000, 'Downloader', '下载已恢复');
} catch (error) {
hilog.error(0x0000, 'Downloader', `恢复下载失败: ${JSON.stringify(error)}`);
}
}
// 取消下载
async cancelDownload(): Promise<void> {
if (!this.downloadTask) return;
try {
await this.downloadTask.remove();
this.isDownloading = false;
this.downloadTask = null;
hilog.info(0x0000, 'Downloader', '下载已取消');
} catch (error) {
hilog.error(0x0000, 'Downloader', `取消下载失败: ${JSON.stringify(error)}`);
}
}
// 处理下载进度
private handleProgress(receivedSize: number, totalSize: number): void {
const now = Date.now();
const elapsed = (now - this.lastProgressTime) / 1000; // 秒
const downloadedDelta = receivedSize - this.lastDownloadedSize;
// 计算下载速度(平滑处理,避免抖动)
const speed = elapsed > 0 ? downloadedDelta / elapsed : 0;
// 计算剩余时间
const remainingSize = totalSize - receivedSize;
const remainingTime = speed > 0 ? remainingSize / speed : 0;
const progress: DownloadProgress = {
downloadedSize: receivedSize,
totalSize: totalSize,
progress: totalSize > 0 ? Math.floor((receivedSize / totalSize) * 100) : 0,
speed: speed,
remainingTime: Math.ceil(remainingTime)
};
this.lastProgressTime = now;
this.lastDownloadedSize = receivedSize;
this.onProgress?.(progress);
}
// 处理下载完成
private handleComplete(filePath: string): void {
this.isDownloading = false;
this.downloadTask = null;
hilog.info(0x0000, 'Downloader', `下载完成: ${filePath}`);
this.onComplete?.(filePath);
}
// 处理下载错误
private handleError(message: string): void {
this.isDownloading = false;
this.downloadTask = null;
hilog.error(0x0000, 'Downloader', message);
this.onError?.(message);
}
// 格式化文件大小
static formatSize(bytes: number): string {
if (bytes < 1024) return `${bytes}B`;
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)}GB`;
}
// 格式化下载速度
static formatSpeed(bytesPerSecond: number): string {
if (bytesPerSecond < 1024) return `${bytesPerSecond.toFixed(0)}B/s`;
if (bytesPerSecond < 1024 * 1024) return `${(bytesPerSecond / 1024).toFixed(1)}KB/s`;
return `${(bytesPerSecond / (1024 * 1024)).toFixed(1)}MB/s`;
}
// 格式化剩余时间
static formatTime(seconds: number): string {
if (seconds < 60) return `${seconds}秒`;
if (seconds < 3600) return `${Math.floor(seconds / 60)}分${seconds % 60}秒`;
return `${Math.floor(seconds / 3600)}时${Math.floor((seconds % 3600) / 60)}分`;
}
}
完整示例:应用内更新UI组件
把检测、下载、进度、安装整合成一个完整的UI组件。
// entry/src/main/ets/components/InAppUpdateComponent.ets
// 应用内更新完整UI组件——从检测到安装的一站式体验
import { hilog } from '@kit.PerformanceAnalysisKit';
import { common } from '@kit.AbilityKit';
import { InAppUpdateManager, UpdateStatus } from '../utils/InAppUpdateManager';
import { UpdateDownloader, DownloadProgress } from '../utils/UpdateDownloader';
@Component
export struct InAppUpdateComponent {
@State status: UpdateStatus = UpdateStatus.IDLE;
@State progress: number = 0; // 下载进度
@State downloadedSize: string = ''; // 已下载大小
@State totalSize: string = ''; // 总大小
@State downloadSpeed: string = ''; // 下载速度
@State remainingTime: string = ''; // 剩余时间
@State updateLog: string = ''; // 更新日志
@State newVersion: string = ''; // 新版本号
@State errorMessage: string = ''; // 错误信息
@State isForceUpdate: boolean = false; // 是否强制更新
private updateManager: InAppUpdateManager = InAppUpdateManager.getInstance();
private downloader: UpdateDownloader = new UpdateDownloader();
aboutToAppear() {
// 初始化
const context = getContext(this) as common.Context;
this.downloader.init(context);
// 监听更新状态变化
this.updateManager.setOnStatusChange((status) => {
this.status = status;
});
// 监听下载进度
this.downloader.onProgress = (progress: DownloadProgress) => {
this.progress = progress.progress;
this.downloadedSize = UpdateDownloader.formatSize(progress.downloadedSize);
this.totalSize = UpdateDownloader.formatSize(progress.totalSize);
this.downloadSpeed = UpdateDownloader.formatSpeed(progress.speed);
this.remainingTime = UpdateDownloader.formatTime(progress.remainingTime);
};
// 监听下载完成
this.downloader.onComplete = (filePath: string) => {
hilog.info(0x0000, 'UpdateUI', `下载完成: ${filePath}`);
this.status = UpdateStatus.DOWNLOADED;
// 触发安装
this.installUpdate(filePath);
};
// 监听下载错误
this.downloader.onError = (error: string) => {
this.errorMessage = error;
this.status = UpdateStatus.FAILED;
};
// 自动检查更新
this.checkUpdate();
}
// 检查更新
async checkUpdate() {
const result = await this.updateManager.checkForUpdate();
if (result.hasUpdate && result.updateInfo) {
this.updateLog = result.updateInfo.updateLog;
this.newVersion = result.updateInfo.versionName;
this.isForceUpdate = result.updateInfo.isForceUpdate;
this.totalSize = UpdateDownloader.formatSize(result.updateInfo.packageSize);
}
}
// 开始下载
startDownload() {
const updateInfo = this.updateManager.getUpdateInfo();
if (!updateInfo) return;
this.status = UpdateStatus.DOWNLOADING;
this.downloader.startDownload({
url: updateInfo.downloadUrl,
fileName: `update_${updateInfo.versionCode}.hap`,
enableMetered: false, // 默认WiFi下载
enableRoaming: false
});
}
// 暂停下载
pauseDownload() {
this.downloader.pauseDownload();
}
// 恢复下载
resumeDownload() {
this.downloader.resumeDownload();
}
// 安装更新
async installUpdate(filePath: string) {
this.status = UpdateStatus.INSTALLING;
try {
// 调用系统安装接口
// HarmonyOS需要通过bundleManager或者隐式Want触发安装
hilog.info(0x0000, 'UpdateUI', `开始安装: ${filePath}`);
// 实际安装逻辑需要根据HarmonyOS版本适配
} catch (error) {
hilog.error(0x0000, 'UpdateUI', `安装失败: ${JSON.stringify(error)}`);
this.errorMessage = '安装失败,请稍后重试';
this.status = UpdateStatus.FAILED;
}
}
build() {
Column({ space: 16 }) {
// 根据状态展示不同UI
if (this.status === UpdateStatus.AVAILABLE) {
this.buildUpdateAvailable()
} else if (this.status === UpdateStatus.DOWNLOADING) {
this.buildDownloading()
} else if (this.status === UpdateStatus.DOWNLOADED) {
this.buildDownloaded()
} else if (this.status === UpdateStatus.FAILED) {
this.buildFailed()
} else if (this.status === UpdateStatus.INSTALLING) {
this.buildInstalling()
}
}
.width('100%')
.padding(20)
}
// 有更新可用
@Builder
buildUpdateAvailable() {
Column({ space: 16 }) {
Text('发现新版本')
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text(`v${this.newVersion}`)
.fontSize(14)
.fontColor('#999999')
// 更新日志
Scroll() {
Text(this.updateLog)
.fontSize(14)
.fontColor('#666666')
.lineHeight(22)
}
.constraintSize({ maxHeight: 150 })
.width('100%')
Text(`安装包大小: ${this.totalSize}`)
.fontSize(13)
.fontColor('#999999')
Row({ space: 12 }) {
if (!this.isForceUpdate) {
Button('稍后再说')
.width('45%')
.backgroundColor('#F5F5F5')
.fontColor('#666666')
.onClick(() => {
// 关闭弹窗
})
}
Button('立即更新')
.width(this.isForceUpdate ? '100%' : '45%')
.backgroundColor('#007DFF')
.onClick(() => this.startDownload())
}
.width('100%')
}
.padding(20)
.backgroundColor(Color.White)
.borderRadius(16)
}
// 下载中
@Builder
buildDownloading() {
Column({ space: 12 }) {
Text('正在下载更新...')
.fontSize(18)
.fontWeight(FontWeight.Bold)
// 进度条
Progress({ value: this.progress, total: 100, type: ProgressType.Linear })
.width('100%')
.color('#007DFF')
// 下载信息
Row() {
Text(`${this.downloadedSize} / ${this.totalSize}`)
.fontSize(12)
.fontColor('#999999')
Text(`${this.downloadSpeed}`)
.fontSize(12)
.fontColor('#999999')
Text(`剩余 ${this.remainingTime}`)
.fontSize(12)
.fontColor('#999999')
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
// 暂停按钮
Button('暂停下载')
.width('100%')
.backgroundColor('#F5F5F5')
.fontColor('#666666')
.onClick(() => this.pauseDownload())
}
.padding(20)
.backgroundColor(Color.White)
.borderRadius(16)
}
// 下载完成
@Builder
buildDownloaded() {
Column({ space: 16 }) {
Text('下载完成')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('#4ECDC4')
Text('更新包已下载完成,点击安装以完成更新')
.fontSize(14)
.fontColor('#666666')
Button('立即安装')
.width('100%')
.backgroundColor('#007DFF')
.onClick(() => {
// 触发安装
})
}
.padding(20)
.backgroundColor(Color.White)
.borderRadius(16)
}
// 下载失败
@Builder
buildFailed() {
Column({ space: 16 }) {
Text('更新失败')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('#FF6B6B')
Text(this.errorMessage || '下载失败,请检查网络后重试')
.fontSize(14)
.fontColor('#666666')
Row({ space: 12 }) {
Button('稍后再说')
.width('45%')
.backgroundColor('#F5F5F5')
.fontColor('#666666')
Button('重试')
.width('45%')
.backgroundColor('#007DFF')
.onClick(() => this.startDownload())
}
.width('100%')
}
.padding(20)
.backgroundColor(Color.White)
.borderRadius(16)
}
// 安装中
@Builder
buildInstalling() {
Column({ space: 16 }) {
LoadingProgress()
.width(48)
.height(48)
.color('#007DFF')
Text('正在安装更新...')
.fontSize(16)
.fontColor('#666666')
Text('安装完成后应用将自动重启')
.fontSize(13)
.fontColor('#999999')
}
.padding(20)
.backgroundColor(Color.White)
.borderRadius(16)
}
}
踩坑与注意事项
坑1:下载目录选择不当
把安装包下载到cache目录——系统清理缓存时可能把你的安装包删了,用户下载了半天白下载。
正确做法:下载到应用的filesDir下,系统不会自动清理。安装完成后再手动删除。
坑2:没有做MD5校验
下载完了直接安装——万一下载过程中文件损坏了呢?安装失败还算好的,万一安装了一个被篡改的包呢?
正确做法:下载完成后校验MD5,确认文件完整后再安装。
// MD5校验示例
import { hash } from '@kit.BasicServicesKit';
async function verifyMd5(filePath: string, expectedMd5: string): Promise<boolean> {
try {
const actualMd5 = await hash.hash(filePath, 'md5');
return actualMd5 === expectedMd5;
} catch (error) {
hilog.error(0x0000, 'Verify', `MD5校验失败: ${JSON.stringify(error)}`);
return false;
}
}
坑3:移动网络下载大文件
用户在4G/5G网络下下载了一个100MB的更新包——流量费谁出?用户可能投诉你。
正确做法:默认只允许WiFi下载。如果用户在移动网络下,弹窗提示"当前使用移动网络,下载可能消耗流量,是否继续?"
坑4:下载过程中应用被杀
下载到50%的时候,用户切到别的应用,系统把你的应用杀了——下载进度丢失。
正确做法:使用系统下载服务(request.downloadFile),即使应用被杀,下载任务仍在系统层面继续。
坑5:安装后没有清理安装包
安装完成了,但100MB的安装包还躺在存储里。用户存储空间越来越少,还不知道是什么占的。
正确做法:安装完成后(或者应用重启后检测到已更新),删除安装包文件。
// 安装后清理
aboutToAppear() {
// 检查是否有残留的更新包
this.cleanOldUpdateFiles();
}
private cleanOldUpdateFiles(): void {
try {
const updateDir = `${this.context.filesDir}/update`;
// 遍历并删除旧更新包
// fs.listFileSync(updateDir).forEach(file => {
// fs.unlinkSync(`${updateDir}/${file}`);
// });
} catch (error) {
// 忽略清理错误
}
}
坑6:并发下载
用户点了"立即更新",但按钮没有及时禁用,用户又点了一次——创建了两个下载任务,浪费流量和存储。
正确做法:下载开始后禁用按钮,或者用状态锁防止重复下载。
HarmonyOS 6适配说明
HarmonyOS 6对应用内更新做了几项调整:
- InAppUpdate API正式发布:HarmonyOS 6提供了官方的InAppUpdate Kit,包含完整的检测、下载、安装API。不再需要自己实现下载逻辑。
// HarmonyOS 6 InAppUpdate Kit 使用示例
import { inAppUpdate } from '@kit.AppKit';
async function checkAndUpdate(): Promise<void> {
try {
// 检测更新
const updateInfo = await inAppUpdate.checkUpdate();
if (updateInfo.hasUpdate()) {
// 获取更新详情
const details = updateInfo.getUpdateInfo();
hilog.info(0x0000, 'InAppUpdate', `新版本: ${details.versionName}`);
// 展示更新弹窗
// ...
// 开始下载
const downloadTask = await inAppUpdate.startDownload(details);
// 监听进度
downloadTask.on('progress', (progress) => {
hilog.info(0x0000, 'InAppUpdate', `下载进度: ${progress}%`);
});
// 等待下载完成
await downloadTask.awaitComplete();
// 触发安装
await inAppUpdate.installUpdate(downloadTask.getFilePath());
}
} catch (error) {
hilog.error(0x0000, 'InAppUpdate', `更新失败: ${JSON.stringify(error)}`);
}
}
-
安装确认增强:HarmonyOS 6的安装确认弹窗会显示更新包的签名信息,帮助用户确认安装包来源可信。
-
后台下载限制:应用在后台时,下载任务会被系统暂停。应用回到前台后自动恢复。
-
存储空间检查:InAppUpdate Kit在下载前会自动检查设备剩余空间,空间不足时给出提示。
-
断点续传支持:官方API内置断点续传能力,下载中断后可以自动恢复。
总结
应用内更新是提升更新转化率的有效手段。核心记住三点:
- 下载要稳:支持断点续传、MD5校验、WiFi/移动网络区分
- 进度要透明:实时展示下载进度、速度、剩余时间
- 安装要安全:校验文件完整性,安装后清理安装包
| 维度 | 评价 |
|---|---|
| 学习难度 | ⭐⭐⭐ 下载管理需要处理多种边界情况 |
| 使用频率 | ⭐⭐⭐⭐ 每次版本更新都会涉及 |
| 重要程度 | ⭐⭐⭐⭐ 直接影响用户更新转化率 |
- 点赞
- 收藏
- 关注作者
评论(0)