当前位置: 首页 > java >正文

uniapp实现app自动更新

uniapp实现app自动更新:

实现步骤:

  1. 需要从后端读取最新版本的相关信息
  2. 前端用户进入首页的时候,需要判断当前版本与后端返回来的版本是否一致,不一致且后端版本大于当前版本的话,就需要提示用户是否需要更新,如果后端传了强制更新,那么就强制用户点击更新

相关js文件:

封装了三个js文件,可以直接用的:

dialog.js:

/*** @Descripttion: app升级弹框* @Version: 1.0.0* @Author: leefine*/import config from './upgrade-config.js'
import upgrade from './upgrade'const {title = '发现新版本',confirmText = '立即更新',cancelTtext = '稍后再说',confirmBgColor = '#409eff',showCancel = true,titleAlign = 'left',descriAlign = 'left',icon
} = config.upgrade;class AppDialog {constructor() {this.maskEl = {}this.popupEl = {}this.screenHeight = 600;this.popupHeight = 230;this.popupWidth = 300;this.viewWidth = 260;this.descrTop = 130;this.viewPadding = 20;this.iconSize = 80;this.titleHeight = 30;this.textHeight = 18;this.textSpace = 10;this.popupContent = []this.apkUrl = '';this.showCancel = true; // 是否显示“稍后再说”按钮}// 显示show(apkUrl, changelog, isForceUpdate = false) {this.showCancel = !isForceUpdate;  // 设置是否显示取消按钮this.drawView(changelog);this.maskEl.show();this.popupEl.show();this.apkUrl = apkUrl;}// 隐藏hide() {this.maskEl.hide()this.popupEl.hide()}// 绘制drawView(changelog) {this.screenHeight = plus.screen.resolutionHeight;this.popupWidth = plus.screen.resolutionWidth * 0.8;this.popupHeight = this.viewPadding * 3 + this.iconSize + 100;this.viewWidth = this.popupWidth - this.viewPadding * 2;this.descrTop = this.viewPadding + this.iconSize + this.titleHeight;this.popupContent = [];if (icon) {this.popupContent.push({id: 'logo',tag: 'img',src: icon,position: {top: '0px',left: (this.popupWidth - this.iconSize) / 2 + 'px',width: this.iconSize + 'px',height: this.iconSize + 'px'}});} else {this.popupContent.push({id: 'logo',tag: 'img',src: '_pic/upgrade.png',position: {top: '0px',left: (this.popupWidth - this.iconSize) / 2 + 'px',width: this.iconSize + 'px',height: this.iconSize + 'px'}});}// 标题if (title) {this.popupContent.push({id: 'title',tag: 'font',text: title,textStyles: {size: '18px',color: '#333',weight: 'bold',align: titleAlign},position: {top: this.descrTop - this.titleHeight - this.textSpace + 'px',left: this.viewPadding + 'px',width: this.viewWidth + 'px',height: this.titleHeight + 'px'}})} else {this.descrTop -= this.titleHeight;}this.drawText(changelog)// 取消if (this.showCancel) {const width = (this.viewWidth - this.viewPadding) / 2;const confirmLeft = width + this.viewPadding * 2;this.drawBtn('cancel', width, cancelTtext)this.drawBtn('confirm', width, confirmText, confirmLeft)} else {this.drawBtn('confirmBox', this.viewWidth, confirmText)}this.drawBox(showCancel)}// 描述内容drawText(changelog) {if (!changelog) return [];const textArr = changelog.split('')const len = textArr.length;let prevNode = 0;let nodeWidth = 0;let letterWidth = 0;const chineseWidth = 14;const otherWidth = 7;let rowText = [];for (let i = 0; i < len; i++) {// 包含中文if (/[\u4e00-\u9fa5]|[\uFE30-\uFFA0]/g.test(textArr[i])) {// 包含字母let textWidth = ''if (letterWidth > 0) {textWidth = nodeWidth + chineseWidth + letterWidth * otherWidth;letterWidth = 0;} else {// 不含字母textWidth = nodeWidth + chineseWidth;}if (textWidth > this.viewWidth) {rowArrText(i, chineseWidth)} else {nodeWidth = textWidth;}} else {// 不含中文// 包含换行符if (/\n/g.test(textArr[i])) {rowArrText(i, 0, 1)letterWidth = 0;} else if (textArr[i] == '\\' && textArr[i + 1] == 'n') {rowArrText(i, 0, 2)letterWidth = 0;} else if (/[a-zA-Z0-9]/g.test(textArr[i])) {// 包含字母数字letterWidth += 1;const textWidth = nodeWidth + letterWidth * otherWidth;if (textWidth > this.viewWidth) {const preNode = i + 1 - letterWidth;rowArrText(preNode, letterWidth * otherWidth)letterWidth = 0;}} else {if (nodeWidth + otherWidth > this.viewWidth) {rowArrText(i, otherWidth)} else {nodeWidth += otherWidth;}}}}if (prevNode < len) {rowArrText(len, -1)}this.drawDesc(rowText)function rowArrText(i, nWidth = 0, type = 0) {const typeVal = type > 0 ? 'break' : 'text';rowText.push({type: typeVal,content: changelog.substring(prevNode, i)})if (nWidth >= 0) {prevNode = i + type;nodeWidth = nWidth;}}}// 描述drawDesc(rowText) {rowText.forEach((item, index) => {if (index > 0) {this.descrTop += this.textHeight;this.popupHeight += this.textHeight;}this.popupContent.push({id: 'content' + index + 1,tag: 'font',text: item.content,textStyles: {size: '14px',color: '#666',align: descriAlign},position: {top: this.descrTop + 'px',left: this.viewPadding + 'px',width: this.viewWidth + 'px',height: this.textHeight + 'px'}})if (item.type == 'break') {this.descrTop += this.textSpace;this.popupHeight += this.textSpace;}})}// 按钮drawBtn(id, width, text, left = this.viewPadding) {let boxColor = confirmBgColor,textColor = '#ffffff';if (id == 'cancel') {boxColor = '#f0f0f0';textColor = '#666666';}this.popupContent.push({id: id + 'Box',tag: 'rect',rectStyles: {radius: '6px',color: boxColor},position: {bottom: this.viewPadding + 'px',left: left + 'px',width: width + 'px',height: '40px'}})this.popupContent.push({id: id + 'Text',tag: 'font',text: text,textStyles: {size: '14px',color: textColor},position: {bottom: this.viewPadding + 'px',left: left + 'px',width: width + 'px',height: '40px'}})}// 内容框drawBox(showCancel) {this.maskEl = new plus.nativeObj.View('maskEl', {top: '0px',left: '0px',width: '100%',height: '100%',backgroundColor: 'rgba(0,0,0,0.5)'});this.popupEl = new plus.nativeObj.View('popupEl', {tag: 'rect',top: (this.screenHeight - this.popupHeight) / 2 + 'px',left: '10%',height: this.popupHeight + 'px',width: '80%'});// 白色背景this.popupEl.drawRect({color: '#ffffff',radius: '8px'}, {top: this.iconSize / 2 + 'px',height: this.popupHeight - this.iconSize / 2 + 'px'});this.popupEl.draw(this.popupContent);this.popupEl.addEventListener('click', e => {const maxTop = this.popupHeight - this.viewPadding;const maxLeft = this.popupWidth - this.viewPadding;const buttonWidth = (this.viewWidth - this.viewPadding) / 2;if (e.clientY > maxTop - 40 && e.clientY < maxTop) {if (this.showCancel) {if (e.clientX > maxLeft - buttonWidth && e.clientX < maxLeft) {// 点击确认upgrade.checkOs(this.apkUrl);}} else {if (e.clientX > this.viewPadding && e.clientX < maxLeft) {upgrade.checkOs(this.apkUrl);}}this.hide();}});}
}export default new AppDialog()

upgrade.js

/*** @Descripttion: app下载更新* @Version: 1.0.0* @Author: leefine*/import config from './upgrade-config.js'
const { upType=0 }=config.upgrade;class Upgrade{// 检测平台checkOs(apkUrl){uni.getSystemInfo({success:(res) => {if(res.osName=="android"){if(upType==1 && packageName){plus.runtime.openURL('market://details?id='+packageName)}else{this.downloadInstallApp(apkUrl)}}else if(res.osName=='ios' && appleId){// apple id 在 app conection 上传的位置可以看到 https://appstoreconnect.apple.complus.runtime.launchApplication({action: `itms-apps://itunes.apple.com/cn/app/id${appleId}?mt=8`}, function(err) {uni.showToast({title:err.message,icon:'none'})})}}  })}// 下载更新downloadInstallApp(apkUrl){const dtask = plus.downloader.createDownload(apkUrl, {}, function (d,status){// 下载完成  if (status == 200){plus.runtime.install(plus.io.convertLocalFileSystemURL(d.filename),{},{},function(error){  uni.showToast({  title: '安装失败',icon:'none'});  })}else{uni.showToast({title: '更新失败',icon:'none'});}});this.downloadProgress(dtask);}// 下载进度downloadProgress(dtask){try{dtask.start(); //开启下载任务let prg=0;let showLoading=plus.nativeUI.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(e){plus.nativeUI.closeWaiting();uni.showToast({title: '更新失败',icon:'none'})}}}export default new Upgrade()

upgrade-config.js

export default {upgrade:{packageName:'',appleId:'',upType:0,timer:24,icon:'/static/logo.png',title:'发现新版本',confirmText:'立即更新',cancelTtext:'稍后再说',confirmBgColor:'#409eff',showCancel:true,titleAlign:'left',descriAlign:'left'}
}

首页发起调用:

import appDialog from '../../node_modules/js_sdk/dialog';
onLoad: function() {this.checkForUpdate() // 检查版本更新},
checkForUpdate() {const that = this;console.log("检查版本更新")// 获取当前版本号是异步的that.getCurrentVersionCode().then(currentVersionCode => {console.log("当前版本号:", currentVersionCode);that.$u.get(`https://${Config.baseUrl}/api/Redpacket/getLatestAppVersion`).then(res => {if (res.status === 'success' && res.data) {const latestVersion = res.data;// 假设 version 是字符串如 "2.0.0",可以用某种方式转成整数版本号,也可以让后端返回 version_codeconst latestVersionCode = parseInt(latestVersion.version.replace(/\D/g, '')) ||0;console.log("服务端返回版本号:", latestVersionCode);if (latestVersionCode > currentVersionCode) {// 根据 force_update 字段决定是否允许取消按钮const isForceUpdate = latestVersion.force_update === 1;appDialog.show(latestVersion.download_url, latestVersion.update_message, isForceUpdate);} else {uni.showToast({title: '当前已是最新版',icon: 'none'});}} else {uni.showToast({title: '版本检查失败',icon: 'none'});}}).catch(err => {console.error('版本检查请求失败:', err);uni.showToast({title: '请求失败,请稍后重试',icon: 'none'});});});},getCurrentVersionCode() {return new Promise((resolve) => {plus.runtime.getProperty(plus.runtime.appid, (wgtinfo) => {resolve(parseInt(wgtinfo.versionCode));});});},

需要注意js文件导入路径,该方法只能在app内调用(包含plus),所以需要运行到安卓基座测试

服务器需要:

我是以表的形式存在数据库中的:

表:

CREATE TABLE `fa_app_version` (`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID',`version` VARCHAR(50) NOT NULL COMMENT '版本号',`force_update` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否强制更新,0 = 否,1 = 是',`update_message` TEXT NOT NULL COMMENT '更新说明',`download_url` VARCHAR(255) NOT NULL COMMENT '下载链接',`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',PRIMARY KEY (`id`),UNIQUE KEY `version` (`version`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='APP版本表';

php代码:

    /*** 获取最新的APP版本信息*/public function getLatestAppVersion(){// 查询id最大的那条APP版本记录$latestVersion = Db::name('app_version')->order('id', 'desc')->limit(1)->field('id, version, force_update, update_message, download_url, created_at, updated_at')->find();return json(['status' => 'success','data' => $latestVersion]);}

前端调用:

that.$u.get(`https://${Config.baseUrl}/api/Redpacket/getLatestAppVersion`).then(res => {if (res.status === 'success' && res.data) {const latestVersion = res.data;// 假设 version 是字符串如 "2.0.0",可以用某种方式转成整数版本号,也可以让后端返回 version_codeconst latestVersionCode = parseInt(latestVersion.version.replace(/\D/g, '')) ||0;console.log("服务端返回版本号:", latestVersionCode);if (latestVersionCode > currentVersionCode) {// 根据 force_update 字段决定是否允许取消按钮const isForceUpdate = latestVersion.force_update === 1;appDialog.show(latestVersion.download_url, latestVersion.update_message, isForceUpdate);} else {uni.showToast({title: '当前已是最新版',icon: 'none'});}} else {uni.showToast({title: '版本检查失败',icon: 'none'});}}).catch(err => {console.error('版本检查请求失败:', err);uni.showToast({title: '请求失败,请稍后重试',icon: 'none'});});

manifest.json文件中需要配置:

在这里插入图片描述

这个,每次打包的时候需要更新这里的值,一般都是向上更新,然后后台数据库里存version的版本号一定要和你打包的时候这里设置的版本号一样。然后需要将你打包的apk上传到服务器public的某个目录下面,保证这个地址能够浏览器打开下载就行。

基本流程就是这样,就是用户进入首页后,会自动取加载是否需要更新的方法,然后根据后台返回的版本号,判断是否需要更新和强制更新,如果需要,那么就点击更新,用户会进入下载页面,我这里有一个下载进度提示,下载完成用户点击安装即可,这样就不需要用户卸载重新安装apk了,管理员通过后台上传新版本的apk,就可以让用户的app实现更新功能

http://www.xdnf.cn/news/1435.html

相关文章:

  • ollama本地搭建大模型
  • 伺服器用什么语言开发呢?做什么用什么?
  • Python流程控制
  • 前端面试场景题
  • Java标识符与关键字终极指南:从基础到高级应用
  • 影刀RPA怎么制作文生图,把网站上图片获取到本地文件夹工作流
  • Flutter 学习之旅 之 flutter 使用 【验证码】输入组件的简单封装
  • 安装Jupyter Notebook 之不断报错 差点放弃版
  • 基于Python将MongoDB文本数据通过text2vec-large-chinese模型向量化并存储到Milvus数据库的完整实现方案
  • “在中国,为中国” 英飞凌汽车业务正式发布中国本土化战略
  • 【调优】log日志海量数据分表后查询速度调优
  • 语法长难句
  • 破茧成蝶:阿里云应用服务器让传统 J2EE 应用无缝升级 AI 原生时代
  • 汽车可变转向比系统的全面认识
  • Python3(7) 数字类型
  • 穿越链路的旅程:深入理解计算机网络中的数据链路层
  • OpenVINO教程(五):实现YOLOv11+OpenVINO实时视频目标检测
  • Qt实战之将自定义插件(minGW)显示到Qt Creator列表的方法
  • Stable Baselines3 结合 gym 训练 CartPole 倒立摆
  • 【C++】vector扩容缩容
  • 2025/4/23 心得
  • 视频图片去水印处理图像 HitPaw Watermark Remover 软件工具WIN
  • MacOS中安装Python(homebrew,pyenv)
  • Java实现插入排序算法
  • 杭电oj(1087、1203、1003)题解
  • 云原生--CNCF-3-核心工具介绍(容器和编排、服务网格和通信、监控和日志、运行时和资源管理,安全和存储、CI/CD等)
  • gtest、gmock的使用
  • Google搜索技巧
  • 【官方正版,永久免费】Adobe Camera Raw 17.2 win/Mac版本 配合Adobe22-25系列软
  • 若依项目部署小结