注册

UNIAPP实现APP自动更新

整体思路和API使用


工作流程



  • App 启动时检查更新
  • 发现新版本时显示更新提示
  • 如果是强制更新,用户必须更新
  • 下载完成后自动安装

API



  • getVersion:自己服务器API,返回版本号、下载地址等信息
  • plus.runtime.getProperty:获取APP当前版本号
  • uni.downloadFile:下载文件
  • plus.runtime.install:安装软件
  • downloadTask.onProgressUpdate:监听下载进度

具体实现


后端getVersionAPI代码


// Version.java
@Data
public class Version {
private String version; // 版本号
private String downloadUrl; // 下载地址
private String description; // 更新说明
private boolean forceUpdate; // 是否强制更新
}

// VersionController.java
@RestController
@RequestMapping("/api/version")
public class VersionController {

@GetMapping("/check")
public Result checkVersion(@RequestParam String currentVersion) {
Version version = new Version();
version.setVersion("1.1.7"); // 最新版本号
version.setDownloadUrl("软件下载地址"); // 下载地址
version.setDescription("1. 修复已知问题\n2. 新增功能");
version.setForceUpdate(true); // 是否强制更新

// 比较版本号
if (compareVersion(currentVersion, version.getVersion()) < 0) {
return Result.success(version);
}

return Result.success(null);
}

// 版本号比较方法
private int compareVersion(String v1, String v2) {
String[] version1 = v1.split("\\.");
String[] version2 = v2.split("\\.");

int i = 0;
while (i < version1.length && i < version2.length) {
int num1 = Integer.parseInt(version1[i]);
int num2 = Integer.parseInt(version2[i]);

if (num1 < num2) return -1;
else if (num1 > num2) return 1;
i++;
}

if (version1.length < version2.length) return -1;
if (version1.length > version2.length) return 1;
return 0;
}
}

其中Version类可以写到数据库中获取


前端update.js封装


// 版本更新工具类 - 使用单例模式确保全局只有一个更新实例
import {
check
} from "../api/util/util";

class AppUpdate {
constructor() {
// 当前应用版本号
this.currentVersion = '';
// 服务器返回的更新信息
this.updateInfo = null;
}

// 检查更新方法
checkUpdate() {
//仅在app环境下运行
// #ifdef APP-PLUS
plus.runtime.getProperty(plus.runtime.appid, (widgetInfo) => {
this.currentVersion = widgetInfo.version;
console.log('当前版本:' + this.currentVersion);
check(this.currentVersion).then(res => {
if (res.data.data) {
this.updateInfo = res.data.data;
this.showUpdateDialog();
}
})
.catch(err => {
console.log(err);
});
});

// #endif
}
showUpdateDialog() {
uni.showModal({
title: '发现新版本',
content: this.updateInfo.description,
confirmText: '立即更新',
cancelText: '稍后再说',
showCancel: !this.updateInfo.forceUpdate, // 强制更新时禁止取消
success: (res) => {
if (res.confirm) {
this.downloadApp();
} else if (this.updateInfo.forceUpdate) {
plus.runtime.quit();
}
}
});
}

downloadApp() {
/* uni.showLoading({
title: '下载中...',
mask: true // 添加遮罩防止重复点击
}); */


// 先打印下载地址,检查 URL 是否正确
console.log('下载地址:', this.updateInfo.downloadUrl);
let showLoading=plus.nativeUI.showWaiting('正在下载');
const downloadTask = uni.downloadFile({
url: this.updateInfo.downloadUrl,
success: (res) => {
console.log('下载结果:', res); // 添加日志
if (res.statusCode === 200) {
console.log('开始安装:', res.tempFilePath); // 添加日志
plus.runtime.install(
res.tempFilePath, {
force: false
},
() => {
console.log('安装成功'); // 添加日志
plus.nativeUI.closeWaiting();
plus.runtime.restart();
},
(error) => {
console.error('安装失败:', error); // 添加错误日志
plus.nativeUI.closeWaiting();
uni.showToast({
title: '安装失败: ' + error.message,
icon: 'none',
duration: 2000
});
}
);
} else {
console.error('下载状态码异常:', res.statusCode); // 添加错误日志
plus.nativeUI.closeWaiting();
uni.showToast({
title: '下载失败: ' + res.statusCode,
icon: 'none',
duration: 2000
});
}
},
fail: (err) => {
console.error('下载失败:', err); // 添加错误日志
plus.nativeUI.closeWaiting();
uni.showToast({
title: '下载失败: ' + err.errMsg,
icon: 'none',
duration: 2000
});
}
});

//监听下载进度
downloadTask.onProgressUpdate((res) => {
console.log('下载进度:', res.progress); // 添加进度日志
if (res.progress > 0) { // 只在有实际进度时更新提示
showLoading.setTitle('正在下载'+res.progress+'%');
}
});
}
}

//单例模式实现
let instance = null;

export default {
getInstance() {
if (!instance) {
instance = new AppUpdate();
}
return instance;
}
}

注意:如果直接使用uni.showLoading来显示下载进度,会造成闪烁效果,所以这里用let showLoading=plus.nativeUI.showWaiting('正在下载');


引用js


以app.vue为例,在启动时触发检查更新


import AppUpdate from '@/utils/update.js';

export default {
onLaunch: function() {
// #ifdef APP-PLUS
AppUpdate.getInstance().checkUpdate();
// #endif
}
}

在 manifest.json 中配置权限


{
"app-plus": {
"distribute": {
"android": {
"permissions": [
"<uses-permission android:name=\"android.permission.INSTALL_PACKAGES\"/>",
"<uses-permission android:name=\"android.permission.REQUEST_INSTALL_PACKAGES\"/>"
]
}
}
}
}

这样封装的优点



  • 代码更加模块化
  • 可以在任何地方调用
  • 使用单例模式避免重复创建
  • 更容易维护和扩展

作者:HuoWang
来源:juejin.cn/post/7457206505021341730

0 个评论

要回复文章请先登录注册