文章目录



 

背景说明

web打包的app(也称为h5+app),是指将基于html5等移动端web技术,开发的web应用打包成的app。区别于原生app,5+app相当于给web应用加上了一层本地程序(ios、android等)的壳子。其原理是,使用了原生程序的webview组件,即在原生程序内部调用内置浏览器,实现应用的核心功能。h5+app打包app使用的hbuiderx打包的,打包相关的方法可以参考,本人文章:
​​​将H5站点打包成app完美攻略​

原生app自动更新实现

原生app主要是基于ios和android的原生平台app,其打包原理,主要源于平台分发管理的不同和行业通用做法。ios是闭源的独立平台,apple store只有一个平台;android碎片化比较严重,世面上有上万家app store。各平台审核机制又不一样。这些方面都决定了ios和android的app,更新策略有所不同。
行业经验
Android 和 iOS 应用的更新都可以不用做,可以让第三方应用商店来帮你做,你只需更改应用的版本就行了。目前 Android 的通用做法是,在应用内检查版本号,通过跟服务器的版本号来对比,版本号不同就更新,具体的做法是可以在应用内写个下载程序,也可以在弹出浏览器来下载。iOS 应用如果要上传到 app store,是不允许在应用内检查更新的,否则不让上架,iOS 的更新更简单,让苹果 app store 来做就行了,你在 build 的时候,改变版本号就行,希望对你有帮助。

以下ios和android app自动更新的实现,都是基于行业最佳实践。

android自动更新实现

在服务器需要一个json或xml文件,如:check.json。

#check.json
[
{
"version":1,
"name":1.0,
"miniVersion":1,
"description":"asdfas/n",
"forceUpdate":false,
"size":3065,
"uri":"h5app-1.0.apk"
},
{
"version":2,
"name":1.1,
"miniVersion":1,
"description":"bbb/n",
"forceUpdate":"true",
"size":3065,
"uri":"h5app-1.1.apk"
},
{
"version":3,
"name":1.1,
"miniVersion":1,
"description":"bbb/n",
"forceUpdate":"false",
"size":3065,
"uri":"h5app-1.1.apk"
}
]



 



以上文件维护平台app的发布版本,基于些版本,提供apk自动更新。

“version”:1, #int 类型版本号,版本是递增的
“name”:1.0,
“miniVersion”:1, #平台支持最小的版本号,小于些的版本都会强制更新
“description”:“asdfas/n”, #版本说明
“forceUpdate”:false, #版本是否强制更新
“size”:3065, #app大小,单位kb
“uri”:“h5app-1.0.apk” #app下载路径

程序文件结构如下:

web打包app(h5+app)版本自动更新的实现_热更新


version:发布apk包目录。

check.json:apk版本配置文件。

相关接口如下

 /**
* h5app 版本检查[android]:方法只提供android版本的app,版本检查,对于ios,依赖apple store的版本管理,进行版本检查和更新。目前,苹果store app上架审核不允许程序进行自动版本更新。
* @return AppVersionBean
* version 更新版本,始终为平台最新版本app
* uri为app下载的url
*/
@RequestMapping("h5app/check")
@ResponseBody
public AppVersionBean h5appCheck(String currentVersion) {
AppVersionBean result = new AppVersionBean();
return result;
}

/**
* h5app下载
* @return
*/
@RequestMapping("h5app")
@ResponseBody
public Object h5app(HttpRequest request) {
//request 请求设备类型,ios 、android做不同的返回
return null;
}

/**
* h5app 应用版本更新下载[android]:
* @param uri 文件名
* @return
*/

/**
* uri为程序下载文件名
*/
@RequestMapping("h5app/{uri}")
public void getH5app(@PathVariable String uri) {
//下载实现

}

/**
* app版本信息实体类
*/
@Data
class AppVersionBean {

Integer version;
String name;
Integer miniVersion;
String description;
//是否强制更新
boolean forceUpdate;
Long size;
String uri;
//是否有更新
boolean isUpdate;
}


 


如上接口,app检查更新接口传入currentVersion。服务端根据currentVersion和服务端版本管理配置(miniVersion,forceUpdate等)确定app是否更新和是否强制更新。
app启动或者提供检查更新按钮,调用后台检查更新接口。

web打包app(h5+app)版本自动更新的实现_app_02

客户端保存currentVersion和忽略版本列表,用于来检查更新,忽略的版本不再提示更新。立即更新,调用下载apk接口下载程序,完成更新。具体本地android代码,不在此提供,网上有很多。

ios自动更新实现

ios apk自动更新,基于apple store来实现。基于现在的上架审核策略,不允许使用检查更新,让苹果 app store 来做就行了,你在 build 的时候,改变版本号就行。

当然如果审核能通过,可以加入检查更新的逻辑,主要思路如下:
1.版本自动更新一般采用API对应的方式 获取当前App Store上版本号 于本地存储的版本号对比。
2.由服务端返回版本控制升级(容易审核不通过)
参考地址(很详细)问题:
​​​ https://www.jianshu.com/p/9e237cd62129​

h5+app的特点说明

h5+app的特点是本地app只是壳子,应用整体在web服务上。app更新比较少,但是也可能涉及打包更新,更新需要在h5的web服务实现。

h5+app自动更新实现

web打包app(h5+app)版本自动更新的实现_热更新_03


如上为打包配置(配置实现版本更新):

var H5_SERVER = “http://www.h5net.com/m/?origin=app&currentVersinotallow=1”;

origin:标志h5,是app访问,配置使web服务能兼容手机web和app使用。

currentVersion:app当前发布版本号。

打包参考:​​将H5站点打包成app完美攻略​

更新的核心逻辑原理跟原生app一致,核心实现检查更新代码,可以参考如下:

//app热更新下载
//假定字符串的每节数都在5位以下
function toNum(a) {
//也可以这样写 var c=a.split(/\./);
var c = a.split('.');
var num_place = ["", "0", "00", "000", "0000"],
r = num_place.reverse();
for(var i = 0; i < c.length; i++) {
var len = c[i].length;
c[i] = r[len] + c[i];
}
var res = c.join('');
return res;
}

var btn = ["确定升级", "取消"];
//获取app系统更新[是否手动点击获取更新]
function appUpdate(Index) {
console.log('appUpdate');
mui.plusReady(function() {
plus.runtime.getProperty(plus.runtime.appid, function(inf) {
ver = inf.version + '';
console.log('ver:' + ver);
var client;
var ua = navigator.userAgent.toLowerCase();
if(/iphone|ipad|ipod/.test(ua)) { //苹果手机
mui.ajax({
type: "get",
dataType: 'json',
url: "https://itunes.apple.com/lookup?id=1462614850", //获取当前上架APPStore版本信息
data: {
id: 1462614850 //APP唯一标识ID
},
contentType: 'application/x-www-form-urlencoded;charset=UTF-8',
success: function(data) {
console.log('data:' + JSON.stringify(data));
var resultCount = data.resultCount;
for(var i = 0; i < resultCount; i++) {
var normItem = data.results[i].version;
console.log('normItem:' + normItem)
if(normItem > ver) {
var _msg = "发现新版本:V" + normItem;
//plus.nativeUI.alert("发现新版本:V" + normItem);
mui.confirm(_msg, '升级确认', btn, function(e) {
if(e.index == 0) { //执行升级操作
document.location.href = 'https://itunes.apple.com/cn/app/%E5%AD%A9%E5%84%BF%E6%AC%A2/id1462614850?mt=8'; //上新APPStore下载地址
}
});
return;
}
}
if(ismanual) {
mui.toast('当前版本号已是最新');
}
return;
}
});
} else if(/android/.test(ua)) {
mui.ajax(ip + "APIVApp/SelectVApp", {
data: {
apkVersion: ver,
},
dataType: 'json',
type: 'get',
timeout: 10000,
success: function(data) {
console.log('data:' + JSON.stringify(data))
console.log(toNum(ver))
if(toNum(data[0]._vname) > toNum(ver)) {
var _msg = "发现新版本:V" + data[0]._vname;
mui.confirm(_msg, '升级确认', btn, function(e) {
if(e.index == 0) { //执行升级操作
downWgt();
}
});
} else {
console.log(Index);
if(Index) {
mui.toast('当前版本号已是最新');
}
return;
}
},
error: function(xhr, type, errerThrown) {
mui.toast('网络异常,请稍候再试');
}
});
}
});
});
}

// 下载wgt文件
function downWgt() {
var wgtUrl = ip + "app/H5750CDB5.wgt";
plus.nativeUI.showWaiting("下载更新文件...");
plus.downloader.createDownload(wgtUrl, {
filename: "_doc/update/"
}, function(d, status) {
if(status == 200) {
console.log("下载更新文件成功:" + d.filename);
installWgt(d.filename); //安装wgt包

} else {
console.log("下载失败!");
plus.nativeUI.alert("下载失败!");
}
plus.nativeUI.closeWaiting();
}).start();
}

function installWgt(path) {
plus.nativeUI.showWaiting("安装更新文件...");
plus.runtime.install(path, {}, function() {
plus.nativeUI.closeWaiting();
console.log("安装更新文件成功!");
plus.nativeUI.alert("应用资源更新完成!", function() {
plus.runtime.restart();
});
}, function(e) {
plus.nativeUI.closeWaiting();
console.log("安装更新文件失败[" + e.code + "]:" + e.message);
plus.nativeUI.alert("安装更新文件失败[" + e.code + "]:" + e.message);
if(e.code == 10) {
alert('请清除临时目录');
}
});
}


 


本文是实践的一些总结,希望对你有所帮助。有什么问题可以留言讨论,有更好的实践方法也欢迎斧正指导。