标签: 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. Go runtime 调度器精讲(十一):总览全局

    原创文章,欢迎转载,转载请注明出处,谢谢. 0. 前言 前面用了十讲介绍了 Go runtime 调度器,这一讲结合一些图在总览下 Go runtime 调度器. 1. 状态转换图 首先是 Gorou ...

  2. Java 8 新特性:Stream 流快速入门

    前言 在 java 中,涉及到对数组.集合等集合类元素的操作时,通常我们使用的是循环的方式进行逐个遍历处理,或者使用 stream 流的方式进行处理. 什么是 Stream? Stream(流)是一个 ...

  3. 加快 hdfs block 块复制的参数调整

    共涉及三个参数: dfs.namenode.replication.max-streams 30 => 70 dfs.namenode.replication.max-streams-hard- ...

  4. Serilog文档翻译系列(五) - 编写日志事件

    日志事件通过 Log 静态类或 ILogger 接口上的方法写入接收器.下面的示例将使用 Log 以便语法简洁,但下面显示的方法同样可用于接口. Log.Warning("Disk quot ...

  5. LeetCode 730. Count Different Palindromic Subsequences (区间DP)

    题意 给一个字符串S,求它所有子序列中不同非空回文串的数量.字符串由 'a' 'b' 'c' 'd' 四个字母组成. 由于题目要求的是不同回文串. abba 的回文串子序列为 a,b,aba,abba ...

  6. qemu的使用

    一.QEMU的运行模式 直接摘抄自己<揭秘家用路由器0day漏洞挖掘技术>,网上查了一下也没有找到令人满意的QEMU的使用说明,就采用这本书上的介绍.如果后期能够找到比较满意的QEMU的使 ...

  7. netCore 配置跨域 封装成文件

    namespace Zhaoxi.Manage.MentApi.Utility.RegisterExt { /// <summary> /// 跨域扩展 /// </summary& ...

  8. ES6箭头函数和普通函数的区别

    书写形式不同,箭头函数的定义方式更加简单 : this指向不同 箭头函数使用父级作用域的this 普通函数执行 window 箭头函数不能使用 new 生成实例对象 : 普通函数的参数是argumen ...

  9. WPF中为Popup和ToolTip使用WindowMaterial特效 win10/win11

    先看效果图: 大致思路是:通过反射获取Popup内部的原生窗口句柄,然后通过前文已经实现的WindowMaterial类来应用窗口特效:对于ToolTip,为了保持其易用性,我使用了附加属性+全局样式 ...

  10. Games104 现代游戏引擎 Picoolo环境搭建 vulkan配置

    0 前言介绍 games104 现代游戏引擎课程是由 GAMES: Graphics And Mixed Environment Symposium 支持的一个课程. 如我们所视,Games并非的含义 ...