UNIAPP实现APP自动更新
整体思路和API使用
工作流程
- App 启动时检查更新
- 发现新版本时显示更新提示
- 如果是强制更新,用户必须更新
- 下载完成后自动安装
API
getVersion
:自己服务器API,返回版本号、下载地址等信息plus.runtime.getProperty
:获取APP当前版本号uni.downloadFile
:下载文件plus.runtime.install
:安装软件downloadTask.onProgressUpdate
:监听下载进度
具体实现
后端getVersion
API代码
// 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
来源:juejin.cn/post/7457206505021341730