标签: uni-app 版本更新


前情

uni-app是我很喜欢的跨平台框架,它能开发小程序,H5,APP(安卓/iOS),对前端开发很友好,自带的IDE让开发体验也很棒,公司项目就是主推uni-app。而是app版本更新是最基本的功能,特记录整个踩坑过程。

版本更新

更新主逻辑

在每次app启动并登录成功的时候做一个版本检测,如果当前版本小于服务端配置的版本号,服务端会告诉是否有更新数据(此处为res.updateFlag,true为需要更新),如果有的话会告诉我去哪里(此次为res下的url)拿更新包数据,客户端再请求res.url拿到更新相关数据(更新包的包地址),如果有更新包地址则弹出弹窗提示用户去更新。

安装更新逻辑为当返回数据url是热更新地址时,则执行热更新逻辑,如果不是则直接调用浏览器去下载安装。

更新关键代码

function checkVersion() {
// #ifdef APP-PLUS
plus.runtime.getProperty(plus.runtime.appid, function(widgetInfo) {
let currVersion = widgetInfo.version;
// 提交版本号和当前appCode,appcode主要是因为我们多个app共用了一套服务端,传此参数是为了告诉服务是哪个app在请求更新
const params = { "appVersionNo": currVersion, "appCode": APPCODE};
// 更新判断接口请求,
uni.request({
url: "更新判断接口地址",
data: params,
success: function(res) {
// console.log("----更新检测数据----:", res);
if (res.updateFlag) {
updateShowModel(res.url, currVersion)
}
}
});
});
// #endif
} /**
* 更新提示
* @param {string} url
* @param {string} currVersion
*/
function updateShowModel(url, currVersion) {
uni.request({ url, success: json => {
//console.log("----当前从服务端拉取的版本信息----:", json);
if (!json || !json.data || !json.data.versions || !json.data.versions[0]) {
return;
}
const versionData = json.data
const versionInfo = versionData.versions[0];
const forcedVersion = versionData.lastForceVersion;
// 是否强制更新,只有当前版本小于服务端返回的强制更新版本号隐藏处弹窗的取消按钮来达到要求用户强制更新
const isForcedUpdate = versionCompare(currVersion, forcedVersion) == -1;
//console.log("----更新相关数据----:",versionData, isForcedUpdate,currVersion,forcedVersion);
uni.showModal({
title: `发现新版本v${versionInfo.version}`,
content: versionInfo.details.join('\n'),
showCancel: !isForcedUpdate,
success: function (res) {
if (res.confirm) {
updateApp(versionData)
//console.log('用户点击确定');
} else if (res.cancel) {
//console.log('用户点击取消');
}
}
});
}})
} /**
* 执行更新操作
* @param {Object} versionData
*/
function updateApp(versionData) {
// 如果有热更新配置文件,则热更
if (versionData.wgtUrl) {
startAndroidUpdate(versionData);
return;
}
switch (uni.getSystemInfoSync().platform) {
case 'android':
// 安卓安装包的apk地址
plus.runtime.openURL(versionData.url);
break
case 'ios':
// ios需要特殊处理,需要服务端给一个plist地址
plus.runtime.openURL(`itms-services://?action=download-manifest&url=${versionData.url}`);
break
}
} /**
* 自动下载安装更新
* @param {Object} versionData 安装包地址相关信息
*/
var showLoading;
function startAndroidUpdate(versionData) {
// 热更新的wgt文件
let url = versionData.wgtUrl;
var dtask = plus.downloader.createDownload(
url, {method:"GET",filename:`_downloads/${APPID}.wgt`},
function(d, status) {
// 下载完成
if (status == 200) {
//console.log("----安装包文件地址----:",d, plus.io.convertLocalFileSystemURL(d.filename), d.filename);
//plus.nativeUI.toast("软件开始安装重启");
showLoading.setTitle(" 安装重启中... ");
plus.runtime.install(d.filename, {force: true}, e => {
plus.nativeUI.closeWaiting();
plus.runtime.restart();
}, function(error) {
console.log("----安装失败01----:", error);
handleWgtInstallFail(versionData);
})
} else {
console.log("----安装失败02----:", d, status);
handleWgtInstallFail(versionData);
}
});
try {
var progress = 0;
showLoading = plus.nativeUI.showWaiting(" 开始下载 "); //创建一个showWaiting对象
dtask.start(); // 开启下载的任务
dtask.addEventListener('statechanged', function(
task,
status
) {
// 给下载任务设置一个监听 并根据状态 做操作
switch (task.state) {
case 1:
showLoading.setTitle(" 正在下载 0% ");
break;
case 2:
showLoading.setTitle("已连接到服务器");
break;
case 3:
progress = parseInt((parseFloat(task.downloadedSize) / parseFloat(task.totalSize)) * 100);
showLoading.setTitle(" 正在下载" + String(progress).padStart(3, " ") + "% ");
break;
case 4:
//plus.nativeUI.closeWaiting();
//下载完成
break;
}
});
} catch (err) {
plus.nativeUI.closeWaiting();
console.log("----安装失败03----:", err);
handleWgtInstallFail(versionData);
}
} /**
* 热更新失败处理
*
*/
function handleWgtInstallFail(versionInfo) {
plus.nativeUI.closeWaiting();
uni.showModal({
title: "温馨提示",
content: "自动更新失败,是否手动更新",
success: function (res) {
if (res.confirm) {
plus.runtime.openURL(versionInfo.url);
}
}
});
} /**
* 版本号比较
* @param {Object} v1 当前版本
* @param {Object} v2 强制更新版本
*/
function versionCompare(v1, v2) {
var GTR = 1; //大于
var LSS = -1; //小于
var EQU = 0; //等于
var v1arr = String(v1).split(".").map(function (a) {
return parseInt(a);
});
var v2arr = String(v2).split(".").map(function (a) {
return parseInt(a);
});
var arrLen = Math.max(v1arr.length, v2arr.length);
var result; //排除错误调用
if (v1 == undefined || v2 == undefined) {
throw new Error();
} //检查空字符串,任何非空字符串都大于空字符串
if (v1.length == 0 && v2.length == 0) {
return EQU;
} else if (v1.length == 0) {
return LSS;
} else if (v2.length == 0) {
return GTR;
} //循环比较版本号
for (var i = 0; i < arrLen; i++) {
result = xxcanghaiComp(v1arr[i], v2arr[i]);
if (result == EQU) {
continue;
} else {
break;
}
}
return result; function xxcanghaiComp(n1, n2) {
if (typeof n1 != "number") {
n1 = 0;
}
if (typeof n2 != "number") {
n2 = 0;
}
if (n1 > n2) {
return GTR;
} else if (n1 < n2) {
return LSS;
} else {
return EQU;
}
}
}

注意事项

开发过程中在ios下热更是可以的,但是在安卓下一直报如下错误:

{
"code": -1201,
"message": "WGT/WGTU文件格式错误"
}

官方论坛也有人遇到,试了很多解决方法都不行,通过打日志发现安卓下下载下来的包是.bin文件。而不是热更需要的wgt文件,一开始怀疑是包没下载完,但明显是在下载完的回调里处理的,后面想到能不能手动改成.wgt文件,plus.downloader.createDownload第二个参数可以指定下载包的文件名,就这样解决了这个问题。

其实安卓下拿到apk地址是可以不需要通过调用浏览器去下载的,直接走热更新逻辑也是可以的。

uni-app开发的app版本更新的更多相关文章

  1. App开发到App Store上架,发布流程。

     http://blog.csdn.net/wojsg001/article/details/12005887 App开发到App Store上架,发布流程. 分类: IOS2013-09-25 11 ...

  2. 开发一个App要多少钱?APP开发报价单,APP开发外包有哪些注意事项-广州达到信息www.ddapp.com.cn

    来源:广州达到信息著作权归广州达到信息所有.商业转载请联系作者获得授权,非商业转载请注明出处. 作为一个APP开发从业者,经常会有人问到:开发一个App要多少钱?下面针对这个问题来好好解答解答正经的谈 ...

  3. 《APP开发》APP规范实例-详细的UI设计方法

    对了一个APP开发初手来说,可能心里有很多的疑惑: 屏幕设计为多宽,宽度是不是应该设置为百分比; 按钮大小多大,怎么排列,文字字体用多大的?什么字体显示好看?图标多大,怎么用色?界面怎么布局?等等很多 ...

  4. APICloud APP前端框架——手机APP开发、APP制作、APP定制平台

    概述 APICloud前端框架,包括api.js和api.css.api.css处理不同平台浏览器的默认样式.api.js是一个JavaScript库.是APICloud为混合移动开发定制的轻量Jav ...

  5. 简单5步说清App软件在线开发、App制作多少钱?

    开发制作一款App,所有人都会首先关心开发一款App多少钱这个问题.从网上的信息来看,花费个几十万是很正常的事情,甚至有人说要花上百万才能制作出一款App.那么App软件的开发制作到底和什么有关?怎么 ...

  6. Hybrid App 开发模式

    开发移动App主要有三种模式:Native. Hybrid 和 Web App. 需要注意的一点是在选择开发模式的时候,要根据你的项目类型(图片类?视频类?新闻类?等),产品业务和人员技术储备等做权衡 ...

  7. Native App开发 与Web App开发(原生与web开发优缺点)

    Native App开发 Native App开发即我们所称的传统APP开发模式(原生APP开发模式),该开发针对IOS.Android等不同的手机操作系统要采用不同的语言和框架进行开发,该模式通常是 ...

  8. APP开发项目思维导图

    APP开发项目思维导图 下载思维导图:APP开发项目.xmind.zip --------------------------------------- APP开发项目 app项目标记: 未启动 功能 ...

  9. Android--观察APP运行日志以及APP的工程目录结构解释

    运行日志 Log:d--便于跟踪调试 APP开发基础 APP的运行环境 第一种情况,就是在Android studio软件客户端上面使用模拟器运行APP 第二种情况,就是使用真实的手机运行APP程序 ...

  10. 巧用第三方快速开发Android App 热门第三方SDK及框架

    巧用第三方快速开发Android App 热门第三方SDK及框架 历经大半年的时间,终于是把这门课程给录制出来了,也就在今天,正式在慕课网上上线了 项目地址:巧用第三方快速开发Android App ...

随机推荐

  1. k8s StorageClass 存储类

    目录 一.概述 1.StorageClass 对象定义 2.StorageClass YAML 示例 二.StorageClass 字段 1.provisioner(存储制备器) 1.1.内置制备器 ...

  2. 使用ValueConverters扩展实现枚举控制页面的显示

    1.ValueConverters 本库包含了IValueConverter接口的的最常用的实现,ValueConverters用于从视图到视图模型的值得转换,某些情况下,可用进行反向转换.里面有一些 ...

  3. Android Qcom USB Driver学习(九)

    本章主要是基于之前的学习,实现一个hidraw的驱动,发现有两种用于识别usb设备的方式,放别是usb_device_id和hid_device_id hid_probe (1)hid_device_ ...

  4. 7-11 leetcode 2619

    请你编写一段代码实现一个数组方法,使任何数组都可以调用 array.last() 方法,这个方法将返回数组最后一个元素.如果数组中没有元素,则返回 -1 . ps:this 环境变量的使用 ,this ...

  5. 谈一谈你对vue指令的理解

    vue指令的本质是给 html 标签新增一些属性 : vue的指令可以分为 3 中类型 : 1. 用于渲染数据的指令,比如 v-for ,v-if ,v-show : 2. 用来交互的指令 ,v-on ...

  6. gost socks5代理

    购买云主机 开放所有tcp端口 配置云主机 https://mirrors.tuna.tsinghua.edu.cn/elrepo/kernel/el8/x86_64/ 选择清华镜像源 [root@i ...

  7. kubernetes日志回滚测试

    kubernetes日志回滚测试 操作节点 podName 查询日志的命令 得到结果 初始pod ms-zipkin-deployment-5949c78884-4x5h7 kubectl logs ...

  8. WinSCP 脚本实现将 jar 包一键上传 Ubuntu 并 docker 部署

    准备 首先,在 Ubuntu 写一个.sh 脚本用于自动更新 jar 包的 docker 容器和镜像,然后在 Windows 写一个.bat 脚本用于上传 jar 包并运行.sh 脚本. deploy ...

  9. javap和字节码

    javap 字节码的基本信息 public class Test { private int age = 10; public int getAge() { return age; } } 在 cla ...

  10. 掌控物体运动艺术:图扑 Easing 函数实践应用

    现如今,前端开发除了构建功能性的网站和应用程序外,还需要创建具有吸引力且尤为流畅交互的用户界面,其中动画技术在其中发挥着至关重要的作用.在数字孪生领域,动画的应用显得尤为重要.数字孪生技术通过精确模拟 ...