前言
uniapp官方推荐的都需要使用它自家的云端基于 uniCloud 云函数实现,使用倒是简单了,但是需要依赖它,还得安装管理后台。实际如果有自己的服务器资源的话,把升级文件放到服务器目录,自己去实现app远程升级,也不是什么麻烦事。虽然应用商店也能发布,但是每次都要审核,在局域网搭建自己的升级服务就显得很有必要了。
这里主要针对打包的app升级,小程序的在线升级简单,在微信公众平台就支持。
官方推荐
App升级中心 uni-upgrade-center,它提供了 App 的版本更新服务。还可以使用其他插件,如dcloudio/uni-app-plus/lib/update
,DCloud 提供的一个用于应用更新的插件。这个插件主要用于在 UniApp 项目中实现应用的在线更新功能。
观影app体验地址:搜索微信公众号[猫青年] ,回复关键词 “影视” 获取下载链接。
如何实现
1.首先是uniapp 生成apk
用Hbuilder 进行打包,可以选择云打包。也可以从网站https://www.yunedit.com/reg?goto=cert 使用自有证书,目测比直接使用云证书要快一些。
2.实现过程
1.准备update.json文件
准备update.json文件,供前端访问升级信息用。
{
"versionCode": 101,
"versionName": "v1.0.1",
"versionDesc": "功能优化",
"appName": "android_myapp.apk",
"downloadUrl": "http://111.xx.xx.xx:8000/static/release/"
}
2. 客户端检查更新
在客户端,你可以使用uni.request
方法来请求这个JSON文件,并对比当前版本和最新版本。如果发现新版本,提示用户更新。
export default {
methods: {
checkForUpdates() {
const currentVersion = '1.1.0'; // 当前应用版本
uni.request({
url: 'https://your-update-server.com/update.json',
success(res) {
const latestVersion = res.data.version;
const updateUrl = res.data.url;
const updateDescription = res.data.description;
if (latestVersion > currentVersion) {
uni.showModal({
title: '发现新版本',
content: updateDescription,
confirmText: '立即更新',
success(res) {
if (res.confirm) {
this.downloadAndInstallUpdate(updateUrl);
}
}
});
} else {
uni.showToast({
title: '已是最新版本',
icon: 'none'
});
}
},
fail(err) {
console.error('检查更新失败', err);
}
});
},
downloadAndInstallUpdate(updateUrl) {
// 下载并安装更新包的逻辑
}
},
onLoad() {
this.checkForUpdates();
}
}
3. 下载更新包
下载更新包可以使用uni.downloadFile
方法。下载完成后,保存文件到本地。
downloadAndInstallUpdate(updateUrl) {
const downloadTask = uni.downloadFile({
url: updateUrl,
success(res) {
if (res.statusCode === 200) {
const filePath = res.tempFilePath;
this.installUpdate(filePath);
} else {
console.error('下载更新包失败');
}
},
fail(err) {
console.error('下载更新包失败', err);
}
});
downloadTask.onProgressUpdate((res) => {
console.log('下载进度', res.progress);
console.log('已经下载的数据长度', res.totalBytesWritten);
console.log('预期需要下载的数据总长度', res.totalBytesExpectedToWrite);
});
}
4. 安装更新包
安装更新包的逻辑会根据不同的平台有所不同。以下是Android平台上的示例:
installUpdate(filePath) {
plus.runtime.install(filePath, {
force: false,
success(res) {
console.log('安装成功');
plus.runtime.restart();
},
fail(err) {
console.error('安装失败', err);
}
});
}
3.详细实现
index.vue首页界面增加使用个popup弹出组件,用来展示升级进度。
<template>
<view class="content">
<!-- 升级弹出框 -->
<uni-popup ref="popup" type="center" :mask-click="false" catchTouchMove>
<view class="popup-content">
<text>{{percentText}}</text>
<progress :percent="progress" :stroke-width="10" />
</view>
</uni-popup>
</view>
</template>
<style scoped>
.popup-content {
text-align: center;
max-width: 500rpx; /* 限定最大高度 */
overflow:hidden;
background-color: #00aaff;
border-radius: 10rpx;
padding: 50px;
}
</style>
在checkVersion()方法里,调用接口checkUpdate去请求后台的update.json文件,获取升级信息。
// 检查更新
export const checkUpdate = async (verCode,verName) => {
try {
console.log('checkUpdate request');
const response = await uni.$http.get('/update.json',{verCode:verCode,verName:verName});
console.log(response);
if (response.statusCode !== 200) {
uni.showToast({
title: '数据请求失败! ',
duration: 1500,
icon: 'none',
});
return [];
}
return response.data;
} catch (error) {
console.error('Network request failed:', error);
uni.showToast({
title: '网络请求失败! ',
duration: 1500,
icon: 'none',
});
return [];
}
};
比较版本号,判断是否应该升级。需要升级则使用uni.showModal弹出提示框。
if(verCode < result.versionCode){
// 更新提醒
uni.showModal({
title: '发现新版本,是否更新',
content: '待更新版本:' + result.versionName +'\n更新说明:'+result.versionDesc,
success: res => {
// 点击确定
if (res.confirm) {
// 打开手机自带浏览器下载
//plus.runtime.openURL(result.downloadUrl+result.appName)
this.downloadUrl = result.downloadUrl+result.appName
console.log('downloadUrl:'+this.downloadUrl)
//显示升级进度
this.showPopup();
if (this.downloadUrl) {
this.isStartDownload = true
//开始下载App
downloadApp(this.downloadUrl, current => {
//下载进度
this.progress = current
}).then(fileName => {
//下载完成
this.isDownloadFinish = true
this.fileName = fileName
if (fileName) {
//自动安装App
this.handleInstallApp()
}
}).catch(e => {
console.log(e, 'e')
})
} else {
uni.showToast({
title: '下载链接不存在',
icon: 'none'
})
}
//this.startProgress();
}
}
});
}
完整实现
<script>
import TodayBoxOffice from '@/components/TodayBoxOffice.vue';
import { downloadApp,installApp } from '@/utils/upgrade.js';
export default {
components: {
TodayBoxOffice
},
data() {
return {
//升级相关
downloadUrl: '', //APP下载链接
isDownloadFinish: false, //是否下载完成
progress: 0, //升级进度条百分比
fileName: '', //下载后app本地路径名称
}
},
async onLoad() {
console.log("onLoad")
//console.log(this.swiperList)
},
computed: {
//百分比文字
percentText() {
let percent = this.progress;
if (typeof percent !== 'number' || isNaN(percent)) return '正在升级中...'
if (percent < 100) return `正在升级中... ${percent}%`
return '下载完成'
}
},
methods: {
checkVersion(){
console.log("checkVersion:")
// 获取本地应用资源版本号
let version = "1.0.0"
let verCode = 0
const systemInfo = uni.getSystemInfoSync();
// 应用程序版本号
// #ifdef APP
version = plus.runtime.version;
verCode = plus.runtime.versionCode;
// #endif
// #ifdef H5
version = systemInfo.appVersion;
// #endif
console.log(version)
checkUpdate(verCode,version).then(result => {
console.log("checkUpdate,result:");
console.log(result);
if(verCode < result.versionCode){
// 更新提醒
uni.showModal({
title: '发现新版本,是否更新',
content: '待更新版本:' + result.versionName +'\n更新说明:'+result.versionDesc,
success: res => {
// 点击确定
if (res.confirm) {
// 打开手机自带浏览器下载
//plus.runtime.openURL(result.downloadUrl+result.appName)
this.downloadUrl = result.downloadUrl+result.appName
console.log('downloadUrl:'+this.downloadUrl)
//显示升级进度
this.showPopup();
if (this.downloadUrl) {
this.isStartDownload = true
//开始下载App
downloadApp(this.downloadUrl, current => {
//下载进度
this.progress = current
}).then(fileName => {
//下载完成
this.isDownloadFinish = true
this.fileName = fileName
if (fileName) {
//自动安装App
this.handleInstallApp()
}
}).catch(e => {
console.log(e, 'e')
})
} else {
uni.showToast({
title: '下载链接不存在',
icon: 'none'
})
}
//this.startProgress();
}
}
});
}
})
},
showPopup() {
this.$refs.popup.open();
},
//安装app
handleInstallApp() {
//下载完成才能安装,防止下载过程中点击
if (this.isDownloadFinish && this.fileName) {
installApp(this.fileName, () => {
//安装成功,关闭升级弹窗
this.$refs.popup.close();
})
}
}
},
}
</script>
/**
* @description H5+下载App
* @param downloadUrl:App下载链接
* @param progressCallBack:下载进度回调
*/
export const downloadApp = (downloadUrl, progressCallBack = () => {}, ) => {
return new Promise((resolve, reject) => {
//创建下载任务
const downloadTask = plus.downloader.createDownload(downloadUrl, {
method: "GET"
}, (task, status) => {
console.log(status,'status')
if (status == 200) { //下载成功
resolve(task.filename)
} else {
reject('fail')
uni.showToast({
title: '下载失败',
duration: 1500,
icon: "none"
});
}
})
//监听下载过程
downloadTask.addEventListener("statechanged", (task, status) => {
switch (task.state) {
case 1: // 开始
break;
case 2: //已连接到服务器
break;
case 3: // 已接收到数据
let hasProgress = task.totalSize && task.totalSize > 0 //是否能获取到App大小
if (hasProgress) {
let current = parseInt(100 * task.downloadedSize / task.totalSize); //获取下载进度百分比
progressCallBack(current)
}
break;
case 4: // 下载完成
break;
}
});
//开始执行下载
downloadTask.start();
})
}
/**
* @description H5+安装APP
* @param fileName:app文件名
* @param callBack:安装成功回调
*/
export const installApp = (fileName, callBack = () => {}) => {
//注册广播监听app安装情况
onInstallListening(callBack);
//开始安装
plus.runtime.install(plus.io.convertLocalFileSystemURL(fileName), {}, () => {
//成功跳转到安装界面
}, function(error) {
uni.showToast({
title: '安装失败',
duration: 1500,
icon: "none"
});
})
}
/**
* @description 注册广播监听APP是否安装成功
* @param callBack:安装成功回调函数
*/
const onInstallListening = (callBack = () => {}) => {
let mainActivity = plus.android.runtimeMainActivity(); //获取activity
//生成广播接收器
let receiver = plus.android.implements('io.dcloud.android.content.BroadcastReceiver', {
onReceive: (context, intent) => { //接收广播回调
plus.android.importClass(intent);
mainActivity.unregisterReceiver(receiver); //取消监听
callBack()
}
});
let IntentFilter = plus.android.importClass('android.content.IntentFilter');
let Intent = plus.android.importClass('android.content.Intent');
let filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_ADDED); //监听APP安装
filter.addDataScheme("package");
mainActivity.registerReceiver(receiver, filter); //注册广播
}
其他资源
UNIapp实现局域网内在线升级的操作方法_vue.js_脚本之家
App升级中心 uni-upgrade-center | uniCloud
uni-app实现资源在线升级/热更新【跨平台开发教程uniapp教程(米饭科技-app小程序h5源码)】_uni-app 热更新 平台自带-CSDN博客
https://blog.51cto.com/u_16353097/8783663
uniapp实现局域网(内网)中APP自动检测版本,弹窗提醒升级_uni-app 怎么自定义版本更新弹窗-CSDN博客
https://juejin.cn/post/7179990162259050554
hbuilderx+香蕉云编生成打包证书和上架全流程 - 简书