在App开发环节,版本自动更新是至关重要的一个环节。尽管官方提供了支持,但管理起来却颇为繁琐,存在不少难点。因此,众多开发者倾向于自行实现版本自动更新,这其中包含了许多值得深入讨论的问题。
前端实现方式
前端自动升级对用户感受影响显著。通过在.js和App.vue中调用更新检查,是一种实现方式。以小型项目为例,比如我2019年加入的那个5人团队开发的项目,由于前期规划不足,导致后续版本更新时界面出现了短暂的闪烁。这不仅是技术上的问题,更凸显了前端实现中细致规划的重要性。再比如,某款新兴App的前端版本更新设计得相当出色,用户几乎察觉不到更新过程。
在开发过程中,前端代码的简洁性同样关键。代码若过于繁杂,会使加载和更新速度减慢。特别是一些大型服务类应用,在高峰时段,前端更新若占用过多资源,将严重影响用户体验。
后端实现的关键
后端在版本自动升级过程中扮演着关键角色。我的后端表格设计得较为简单,操作上确实便于版本回退。之前曾负责一个电商App的版本管理,当时设计的后端版本管理表格,每个字段都十分明确,比如版本号、更新时间、更新内容概要等。相应的SQL语句编写起来十分明了,便于查询和修改,能快速找到所需信息。基于这样的基础,增加一个查询当前版本的接口便变得容易多了。
后端设计不能太简单。曾经有一款社交App,因为后端版本管理不够复杂,在更新时出现了数据丢失的问题。2022年的一次大更新,由于缺乏细致的版本管理操作(创建、读取、更新、删除),导致一些用户信息关联出现了错误。
import request from "@/utils/common.js";
import config from '../config.js'
function check(param = {}) {
// 合并默认参数
param = Object.assign({
title: "检测到有新版本!",
content: "请升级app到最新版本!",
canceltext: "暂不升级",
oktext: "立即升级"
}, param)
plus.runtime.getProperty(plus.runtime.appid, (widgetInfo) => {
let platform = plus.os.name.toLocaleLowerCase()
request.request('/app/versionConfig/getCurrent', {}, 'get', function (result) {
let data = result.data ? result.data : null;
if (widgetInfo.version === data.versionCode) {
return;
}
if (result.code == 200) {
if (platform == 'ios') {
// 如果是ios,则跳转到appstore
plus.runtime.openURL(result.data.data.url)
return;
}
// android进行如下操作
uni.showModal({
title: param.title,
content: data.log ? data.log : param.content,
showCancel: data.force ? false : true,
confirmText: param.oktext,
cancelText: param.canceltext,
success: res => {
if (!res.confirm) {
console.log('取消了升级');
plus.runtime.quit();
}
if (data.shichang === 1) {
//去应用市场更新
plus.runtime.openURL(data.shichangurl);
plus.runtime.restart();
} else {
// 清除缓存
request.clearLogin();
// 开始下载
// 创建下载任务
var dtask = plus.downloader.createDownload(config.baseUrl + data.versionUrl, {
filename: "_downloads/"
},
function (d, status) {
// 下载完成
if (status == 200) {
plus.runtime.install(d.filename, {
force: true
}, function () {
//进行重新启动;
plus.runtime.restart();
}, (e) => {
uni.showToast({
title: '安装升级包失败:' + JSON
.stringify(e),
icon: 'none'
})
});
} else {
this.tui.toast("下载升级包失败,请手动去站点下载安装,错误码: " +
status);
}
});
let view = new plus.nativeObj.View("maskView", {
backgroundColor: "rgba(0,0,0,.6)",
left: ((plus.screen.resolutionWidth / 2) - 45) +
"px",
bottom: "80px",
width: "90px",
height: "30px"
})
view.drawText('开始下载', {}, {
size: '12px',
color: '#FFFFFF'
});
view.show()
dtask.addEventListener("statechanged", (e) => {
if (e && e.downloadedSize > 0) {
let jindu = ((e.downloadedSize / e.totalSize) *
100).toFixed(2)
view.reset();
view.drawText('进度:' + jindu + '%', {}, {
size: '12px',
color: '#FFFFFF'
});
}
}, false);
dtask.start();
}
}
})
}
})
});
}
export default {
check
}
云端云函数的考量
官方推出的应用升级服务,其核心是依托云端的云函数技术。这种做法看似便捷,实则给项目增添了额外的服务端负担。我之前参与过一个企业级应用的开发,就使用了这种云函数技术。结果发现,我们不仅要管理主服务端,还得持续关注云函数服务端的状态。举例来说,如果云函数服务端出现故障——比如2021年就曾发生过这样的情况——那么整个App的版本升级就会受到影响,进而减缓用户获取最新版本的速度。
<script>
import checkappupdate from 'common/checkappupdate.js'
export default {
onLaunch: function () {
checkappupdate.check({
title: '检测到有新版本!',
content: '请升级app到最新版本!',
canceltext: '暂不升级',
oktext: '立即升级'
});
}
}
</script>
云函数的费用同样不容忽视,特别是对于预算有限的创业项目来说,这些费用每月累积起来,数额相当可观。
自定义版本升级的优势
自定义版本的自动更新给实际开发带来了诸多便利。其中,便于调整是一个关键因素。以我之前开发的一个工具类App为例,随着功能的持续扩展,每次更新都需要对不同的地方进行修改。这时,自定义版本的升级功能就能迅速调整更新逻辑。例如,在新增功能时,我们只需对相关功能的版本更新进行特定的代码处理。
减少对云函数的依赖,相应地降低了故障风险。以校内自用的管理App为例,升级到自定义版本后,由于学校网络环境相对稳定,很少因外部服务端问题而影响升级流程。
自定义版本升级虽非易事,文中指出模块代码调整可能较为复杂。这亦是众多开发者常遇的挑战。以2020年对一款阅读App进行自定义升级为例,针对安卓系统进行了版本更新,却发现在iOS系统上出现显示故障,不得不进行大量代码调试。
多设备兼容性存在难题,手机和平板等设备因尺寸不同,对升级后界面的展示效果有不同要求,为此,必须投入大量精力确保定制版在各设备上都能顺畅运行。
展望App版本升级的发展
移动应用市场持续壮大,App的更新方法也在持续优化。未来,对更智能的版本升级的需求将会上升。比如,有些App已经开始尝试根据用户的使用习惯来推送个性化的版本更新。比如视频类App,若发现用户常看高清视频,新版本升级时会优先推送相关的高清内容。开发者面临选择:是继续探索更优的自定义升级方式,还是尝试官方或其他新的升级模式?欢迎读者在评论区发表意见,并点赞及转发本文。
drop table if exists biz_version_config;
/*==============================================================*/
/* Table: biz_version_config */
/*==============================================================*/
create table biz_version_config
(
id int(11) not null auto_increment comment '主键',
version_code varchar(255) comment '版本号',
version_url varchar(255) comment '下载地址',
version_remark varchar(255) comment '版本备注',
is_current int(11) comment '当前版本 1-是 2-否',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
update_by varchar(64) default '' comment '更新者',
update_time datetime comment '更新时间',
status tinyint(1) comment '状态 1-正常2-删除',
primary key (id)
)
ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COMMENT = 'app版本配置' ROW_FORMAT = Dynamic;
alter table wm_version_config comment 'app版本配置';