uniapp 实现APP强更新,热更新
这里我封装了一个版本更新的组件
项目版本号在manifest.json---基础配置中查看
应用版本名称 2.1.07 这种三段式的就是大版本号,一般用于版本强制更新,比如重新安装apk包
应用版本号 2107 就是小版本号 ,一般用于热更新
下面是代码
首先在项目入口地址App.vue 界面获取设备信息,版本号信息,保存下来
onLaunch: function() {
// 获取手机设备信息 const res = uni.getSystemInfoSync(); uni.setStorageSync('platform', res.platform); // #ifdef APP-PLUS
// 获取应用版本信息 plus.runtime.getProperty(plus.runtime.appid, function(inf) { uni.setStorageSync('version', inf.version); // 大版本号 uni.setStorageSync('versionCode', inf.versionCode); // 小版本号 }); let uuid = plus.device.uuid; // #endif },
下面是强更新,热更新代码
这里是我写成了一个下载更新的组件 downloadUp.vue, 组件放在项目首页,如果版本需要更新,就会唤起组件弹窗,让用户手动强制更新,热更新的话,进入首页就自动触发了
created() {
// 获取设备是安卓还是ios const appUpdate = uni.getStorageSync('platform'); // #ifdef APP-PLUS this.type = appUpdate == 'android' ? 2 : 1; this.checkUpdate(); // 判断是否需要更新 // #endif },
checkUpdate() { const self = this, localAppVersonName = uni.getStorageSync('version'), localAppVerson = uni.getStorageSync('versionCode'); // #ifdef APP-PLUS plus.runtime.getProperty(plus.runtime.appid, function(widgetInfo) { getUpgrade({ appType: self.type }).then(res => { const { data } = res.data; if (res.data.code !== '200') return; // app大版本更新 if (data.apkVersion && comparisonVersionHandler(localAppVersonName, data.apkVersion.appVersonName)) { uni.hideTabBar() // 隐藏首页底部tabbar 按钮 self.updateApkObj = data.apkVersion; self.msg = data.apkVersion.versonLog; self.bannerShow = true; // 显示弹窗 return; // app热更新 } else if (data.wgtVersion && comparisonVersionHandler(localAppVerson, data.wgtVersion.appVerson)) { self.downloadWgt(data.wgtVersion.downloadUrl); return; } else { return; } }); }); // #endif },
comparisonVersionHandler 是判断版本号是否需要更新的方法
comparisonVersionHandler = (reqV, curV) => { /* 版本号名比较是否需要升级 curV string :当前最新 reqV string :之前 返回 true 表示需要升级 */ if (curV && reqV) { curV = String(curV); reqV = String(reqV); //将两个版本号拆成数字 let arr1 = curV.split('.'), //當前 arr2 = reqV.split('.'); let minLength = Math.min(arr1.length, arr2.length), position = 0, diff = 0; //依次比较版本号每一位大小,当对比得出结果后跳出循环 while (position < minLength && ((diff = parseInt(arr1[position]) - parseInt(arr2[position])) == 0)) { position++; } diff = (diff != 0) ? diff : (arr1.length - arr2.length); //若curV大于reqV,则返回true return diff > 0; } else { //输入为空 return false; } };
代码热更新
downloadWgt(updateWgtUrl) { // 热更新 // 下载wgt方法 const that = this; // 更新文件 wgt 文件地址 plus.nativeUI.showWaiting('正在更新...'); // 打开系统等待对话框 plus.downloader .createDownload( updateWgtUrl, { filename: '_doc/update/' }, function(d, status) { if (status == 200) { that.installWgt(d.filename); } plus.nativeUI.closeWaiting(); // 关闭系统等待对话框
}
)
.start();
},
installWgt(path) {
// 安装wgt方法
plus.nativeUI.showWaiting('安装文件...');
plus.runtime.install(
path,
{ force: true },
function() {
plus.nativeUI.closeWaiting();
plus.nativeUI.alert('应用资源更新完成!', function() {
plus.runtime.restart(); // 重启APP
});
},
function(e) {
plus.nativeUI.closeWaiting();
}
);
},
強更新
goUpdate(updateApkObj) { if (this.type == 2) { // 安卓端 // 弹出系统等待对话框 var dtask = plus.downloader.createDownload(updateApkObj.downloadUrl, {}, function(d, status) { this.bannerShow = false; // 下载完成 if (status == 200) { plus.runtime.install(plus.io.convertLocalFileSystemURL(d.filename), {}, {}, function(error) { uni.showToast({ title: '安装失败', mask: false, duration: 1500 }); }); } else { uni.showToast({ title: '更新失败', mask: false, duration: 1500 }); } }); try { dtask.start(); // 开启下载的任务 let prg = 0; const showLoading = plus.nativeUI.showWaiting('正在下载'); //创建一个showWaiting对象 dtask.addEventListener('statechanged', function(task, status) { // 给下载任务设置一个监听 并根据状态 做操作 switch (task.state) { case 1: showLoading.setTitle('正在下载'); break; case 2: showLoading.setTitle('已连接到服务器'); break; case 3: prg = parseInt((parseFloat(task.downloadedSize) / parseFloat(task.totalSize)) * 100); showLoading.setTitle(' 正在下载' + prg + '% '); break; case 4: plus.nativeUI.closeWaiting(); //下载完成 break; } }); } catch (err) { plus.nativeUI.closeWaiting(); uni.showToast({ title: '更新失败-03', mask: false, duration: 1500 }); } } else if (this.type == 1) { // ios跳转到app store plus.runtime.openURL(updateApkObj.downloadUrl); } }
下面是全部代碼
<template> <view class="content" v-if="bannerShow"> <!-- 弹出层 --> <view class="uni-banner"> <image src="/static/img/startUpHeader01.png" class="startUpHeaderLogo" /> <view class="banner_box"> <view class="startUpHeaderBox h6">发现新版本</view> <view class="conter">{{ msg }}</view> <view class="banner_foot"><button @click="goUpdate(updateApkObj)" class="banner_foot_button">前往升级</button></view> </view> </view> <view class="uni-mask"></view> </view> </template> <script> import { comparisonVersionHandler } from '@/common/js/tools'; import { getUpgrade } from '@/common/js/apis'; export default { data() { return { bannerShow: false, // 是否需要強制更新app msg: '', // 更新描述 type: '', // 设备类型 android:2, ios:1 updateApkObj: null // 后台数据 }; }, created() { const appUpdate = uni.getStorageSync('platform'); // #ifdef APP-PLUS this.type = appUpdate == 'android' ? 2 : 1; this.checkUpdate(); // #endif }, methods: { checkUpdate() { const self = this, localAppVersonName = uni.getStorageSync('version'), localAppVerson = uni.getStorageSync('versionCode'); // #ifdef APP-PLUS plus.runtime.getProperty(plus.runtime.appid, function(widgetInfo) { getUpgrade({ appType: self.type }).then(res => { const { data } = res.data; if (res.data.code !== '200') return; // 大版本更新 if (data.apkVersion && comparisonVersionHandler(localAppVersonName, data.apkVersion.appVersonName)) { uni.hideTabBar() self.updateApkObj = data.apkVersion; self.msg = data.apkVersion.versonLog; self.bannerShow = true; return; // app热更新 } else if (data.wgtVersion && comparisonVersionHandler(localAppVerson, data.wgtVersion.appVerson)) { self.downloadWgt(data.wgtVersion.downloadUrl); return; } else { return; } }); }); // #endif }, downloadWgt(updateWgtUrl) { // 热更新 // 下载wgt方法 const that = this; // 更新文件 wgt 文件地址 plus.nativeUI.showWaiting('正在更新...'); plus.downloader .createDownload( updateWgtUrl, { filename: '_doc/update/' }, function(d, status) { if (status == 200) { that.installWgt(d.filename); // 安装wgt方法 } plus.nativeUI.closeWaiting(); } ) .start(); }, installWgt(path) { // 安装wgt方法 plus.nativeUI.showWaiting('安装文件...'); plus.runtime.install( path, { force: true }, function() { plus.nativeUI.closeWaiting(); plus.nativeUI.alert('应用资源更新完成!', function() { plus.runtime.restart(); }); }, function(e) { plus.nativeUI.closeWaiting(); } ); }, goUpdate(updateApkObj) { if (this.type == 2) { // 弹出系统等待对话框 var dtask = plus.downloader.createDownload(updateApkObj.downloadUrl, {}, function(d, status) { this.bannerShow = false; // 下载完成 if (status == 200) { plus.runtime.install(plus.io.convertLocalFileSystemURL(d.filename), {}, {}, function(error) { uni.showToast({ title: '安装失败', mask: false, duration: 1500 }); }); } else { uni.showToast({ title: '更新失败', mask: false, duration: 1500 }); } }); try { dtask.start(); // 开启下载的任务 let prg = 0; const showLoading = plus.nativeUI.showWaiting('正在下载'); //创建一个showWaiting对象 dtask.addEventListener('statechanged', function(task, status) { // 给下载任务设置一个监听 并根据状态 做操作 switch (task.state) { case 1: showLoading.setTitle('正在下载'); break; case 2: showLoading.setTitle('已连接到服务器'); break; case 3: prg = parseInt((parseFloat(task.downloadedSize) / parseFloat(task.totalSize)) * 100); showLoading.setTitle(' 正在下载' + prg + '% '); break; case 4: plus.nativeUI.closeWaiting(); //下载完成 break; } }); } catch (err) { plus.nativeUI.closeWaiting(); uni.showToast({ title: '更新失败-03', mask: false, duration: 1500 }); } } else if (this.type == 1) { // ios跳转到app store plus.runtime.openURL(updateApkObj.downloadUrl); } } } }; </script> <style lang="scss" scoped> .content, #img { width: 100%; height: 100%; } .content .con { font-size: 0; display: flex; align-items: center; } #info { position: absolute; top: 0; left: 0; width: 72rpx; height: 72rpx; line-height: 72rpx; border-radius: 50%; background-color: rgba(0, 0, 0, 0.3); text-align: center; color: #fff; font-size: 24rpx; } .wrapper { width: 36rpx; height: 72rpx; position: absolute; top: 0; overflow: hidden; } .right { right: 0; } .left { left: 0; } /* 弹出层形式的广告 */ .uni-banner { display: flex; align-items: center; flex-direction: column; position: fixed; left: 50%; top: 50%; z-index: 1000; transform: translate(-50%, -50%); width: 80%; .startUpHeaderLogo { width: 256rpx; height: 194rpx; } .banner_box { width: 100%; background: #fff; border-radius: 20rpx; } .startUpHeaderBox { width: 100%; height: 96rpx; background-image: url('/static/img/startUpHeader02.png'); background-position: left top; background-repeat: no-repeat; background-size: cover; } } .banner_box .h6 { text-align: center; line-height: 96rpx; font-size: 36rpx; color: #fff; font-weight: bold; } .banner_box .conter { font-size: 28rpx; color: #333; margin: 48rpx; } .banner_foot { width: 238rpx; margin: 48rpx auto; } .banner_foot_button { background-image: linear-gradient(to right, #3aaf7a, #41bf7f); line-height: 72rpx; height: 72rpx; font-size: 30rpx; color: #fff; font-weight: bold; border-radius: 40rpx; border: none; box-shadow: 0 8rpx 20rpx 0 rgba(46, 189, 88, 0.36); } .banner_foot_button:active { background-image: linear-gradient(to right, #41bf7f, #3aaf7a); } .uni-mask { background: rgba(0, 0, 0, 0.6); position: fixed; width: 100%; height: 100%; left: 0; top: 0; z-index: 2; } </style>