【uniapp】uniapp安卓APP在线自动升级功能

纯前端实现uniapp写的安卓APP跟IOS在线自动升级功能

关于Uniapp自动升级用到的阿里云要收费的问题
使用的UI框架为UVIEW2.0
用到的请求等

总结下思路 就是通过获取XML文件返回来的信息来判断要不要更新,用到的前端代码是在
链接: 升级中心 uni-upgrade-center - App.基础上修改的,后面附上修改后的代码
提取XML文件返回来的字段来对比版本号,以及获取下载地址等

创建升级前端组件

1.在项目components新建名为upgrade-center组件,新建up.js文件

在这里插入图片描述

2.在pages.json页面新增增加的组件页面路径

代码:

// 页面路径
{
			"path": "components/upgrade-center/upgrade-center",
			"style": {
				"disableScroll": true,
				"app-plus": {
					"backgroundColorTop": "transparent",
					"background": "transparent",
					"titleNView": false,
					"scrollIndicator": false,
					"popGesture": "none",
					"animationType": "fade-in",
					"animationDuration": 200
		
				}
			}
		},

在这里插入图片描述

3.up.js代码
const PACKAGE_INFO_KEY = '__package_info__'

export default function() {
	// #ifdef APP-PLUS
	let appversion = ''
	plus.runtime.getProperty(plus.runtime.appid, (wgtinfo) => {
		console.log("A版本号", wgtinfo); //应用版本号
		appversion = wgtinfo.version
	})
	let xmlstr = null
	uni.$u.http.get('填写为你的XML请求地址', {
		/* 会加在url上 */
		header: {},
		/* 会与全局header合并,如有同名属性,局部覆盖全局 */
		dataType: 'json',
		// 注:如果局部custom与全局custom有同名属性,则后面的属性会覆盖前面的属性,相当于Object.assign(全局,局部)
		// #ifndef MP-ALIPAY
		responseType: 'text',
		// #endif
	}).then(res => {
		let version = res.data.match(/version(\S*)version/)[1].replace(/[></]/g, '') // 版本
		
		// 整包更新 格式
		// let INFO_KEY_Up = {
		// 	"message": "整包更新",
		// 	"is_silently": false,
		// 	"is_mandatory": true,
		// 	"appid": "__uni__96d9bbc",
		// 	"name": "银通车辆信息平台",
		// 	"title": "",
		// 	"contents": "",
		// 	"platform": ["Android"],
		// 	"version": "",
		// 	"url": "",
		// 	"stable_publish": true,
		// 	"type": "native_app",
		// 	"uni_platform": "android",
		// 	"create_env": "upgrade-center",
		// 	"create_date": 1673250560025
		// }

		// WGT更新 格式

		// let INFO_KEY_Up = {
		// 	"message": "wgt更新",
		// 	"is_silently": false,
		// 	"is_mandatory": false,
		// 	"appid": "__uni__96d9bbc",
		// 	"name": "银通车辆信息平台",
		// 	"title": "",
		// 	"contents": "",
		// 	"platform": ["Android"],
		// 	"version": "",
		// 	"min_uni_version": "",
		// 	"url": "",
		// 	"stable_publish": true,
		// 	"type": "wgt",
		// 	"uni_platform": "android",
		// 	"create_env": "upgrade-center",
		// 	"create_date": 1673250560025
		// }

		// WGT更新 

		let INFO_KEY_Up = {
			"message": "wgt更新",
			"is_silently": false,
			"is_mandatory": false,
			"appid": "更改自己的APPID",
			"name": "更改自己的软件名字",
			"title": "",
			"contents": "",
			"platform": ["Android"],
			"version": "",
			"min_uni_version": "",
			"url": "",
			"stable_publish": true,
			"type": "",
			"uni_platform": "android", //安卓
			"create_env": "upgrade-center", 
			"create_date": 1673856560025
		}
		let INFO_KEY = JSON.stringify(res.data).replace(/\s*/g, "") //获取XML内容
		console.log(INFO_KEY);

		INFO_KEY_Up.version = INFO_KEY.match(/version(\S*)version/)[1].replace(/[></]/g, '') // 版本
		INFO_KEY_Up.title = INFO_KEY.match(/title(\S*)title/)[1].replace(/[></]/g, '') // 版本
		INFO_KEY_Up.contents = INFO_KEY.match(/contents(\S*)contents/)[1].replace(/[></]/g, '') // WGT版本
		INFO_KEY_Up.url = INFO_KEY.match(/url(\S*)url/)[1].replace(/[><]/g, '')
		INFO_KEY_Up.url = INFO_KEY_Up.url.substr(0, INFO_KEY_Up.url.length - 1);
		INFO_KEY_Up.type = INFO_KEY.match(/type(\S*)type/)[1].replace(/[></]/g, '') // WGT或则整包更新

		if (appversion >= version) {
			uni.$u.toast("当前已经是最新版本")
		} else {
			let xt = uni.$u.os()
			console.log(xt, "当前设备类型");
			if (xt == 'android') {
				uni.$u.toast("发现新版本,请及时更新")
			}
			// IOS整包跳转到应用商城更新  //没有此需求可以注释
			if (xt == 'ios' && INFO_KEY_Up.type == 'native_app') {
				uni.$u.toast("发现新版本,正在前往APPStore更新")
				setTimeout(function() {
					let appleId = '填写你的IOS商店的ID'
					plus.runtime.launchApplication({
						action: `itms-apps://itunes.apple.com/cn/app/id${appleId}?mt=8`
					}, function(e) {
						console.log('Open system default browser failed: ' + e
							.message);
					});
				}, 1200);
				return
			}
		}
		if (appversion < version) {
			// let curPage = getCurrentPages();
			// let route = curPage[curPage.length - 1].route; //获取当前页面的路由
			console.log(INFO_KEY_Up, "当前更新版本信息");
			uni.setStorageSync('PACKAGE_INFO_KEY', INFO_KEY_Up)
			setTimeout(function() {
				let curPage = getCurrentPages();
				let route = curPage[curPage.length - 1].route; //获取当前页面的路由
				if (route != 'components/UPAPP/UPAPP') {

					let xt = uni.$u.os()
					console.log(xt, "当前设备类型");
					if (xt == 'android') {
						uni.reLaunch({
							url: `/components/upgrade-center/upgrade-center`,
							fail: (err) => {
								console.error('更新弹框跳转失败', err)
							}
						})
						return
					}
					if (xt == 'ios' && INFO_KEY_Up.type == 'wgt') {
						uni.reLaunch({
							url: `/components/upgrade-center/upgrade-center`,
							fail: (err) => {
								console.error('更新弹框跳转失败', err)
							}
						})
						return
					}
				}

			}, 500);

		} else {
			return
		}

	}).catch(err => {})
	// #endif
}


4.upgrade-center.vue代码
<template>
	<view class="mask flex-center">
		<view class="content botton-radius">
			<view class="content-top">
				<text class="content-top-text">{{title}}</text>
				<image class="content-top" style="top: 0;" width="100%" height="100%"
					src="../../uni_modules/uni-upgrade-center-app/images/bg_top.png">
				</image>
			</view>
			<view class="content-header"></view>
			<view class="content-body">
				<view class="title">
					<text>{{subTitle}}</text>
					<!-- <text style="padding-left:20rpx;font-size: 0.5em;color: #666;">v.{{version}}</text> -->
				</view>
				<view class="body">
					<scroll-view class="box-des-scroll" scroll-y="true">
						<text class="box-des">
							{{contents}}
						</text>
					</scroll-view>
				</view>
				<view class="footer flex-center">

					<template>
						<template v-if="!downloadSuccess">
							<view class="progress-box flex-column" v-if="downloading">
								<progress class="progress" border-radius="35" :percent="downLoadPercent"
									activeColor="#3DA7FF" show-info stroke-width="10" />
								<view style="width:100%;font-size: 28rpx;display: flex;justify-content: space-around;">
									<text>{{downLoadingText}}</text>
									<text>({{downloadedSize}}/{{packageFileSize}}M)</text>
								</view>
							</view>

							<button v-else class="content-button" style="border: none;color: #fff;" plain
								@click="updateApp">
								{{downLoadBtnText}}
							</button>
						</template>
						<button v-else-if="downloadSuccess && !installed" class="content-button"
							style="border: none;color: #fff;" plain :loading="installing" :disabled="installing"
							@click="installPackage">
							{{installing ? '正在安装……' : '下载完成,立即安装'}}
						</button>

						<button v-if="installed && isWGT" class="content-button" style="border: none;color: #fff;" plain
							@click="restart">
							安装完毕,点击重启
						</button>
					</template>
				</view>
			</view>

			<!-- <image v-if="!is_mandatory" class="close-img"
				src="../../uni_modules/uni-upgrade-center-app/images/app_update_close.png" @click.stop="closeUpdate">
			</image>

			<image v-if="is_mandatory" class="close-img"
				src="../../uni_modules/uni-upgrade-center-app/images/app_update_close.png" @click="closeUp">
			</image> -->
		</view>
	</view>
</template>

<script>
	const localFilePathKey = '__localFilePath__'
	const platform_iOS = 'iOS';
	let downloadTask = null;
	let openSchemePromise

	/**
	 * 对比版本号,如需要,请自行修改判断规则
	 * 支持比对	("3.0.0.0.0.1.0.1", "3.0.0.0.0.1")	("3.0.0.1", "3.0")	("3.1.1", "3.1.1.1") 之类的
	 * @param {Object} v1
	 * @param {Object} v2
	 * v1 > v2 return 1
	 * v1 < v2 return -1
	 * v1 == v2 return 0
	 */
	function compare(v1 = '0', v2 = '0') {
		v1 = String(v1).split('.')
		v2 = String(v2).split('.')
		const minVersionLens = Math.min(v1.length, v2.length);

		let result = 0;
		for (let i = 0; i < minVersionLens; i++) {
			const curV1 = Number(v1[i])
			const curV2 = Number(v2[i])

			if (curV1 > curV2) {
				result = 1
				break;
			} else if (curV1 < curV2) {
				result = -1
				break;
			}
		}

		if (result === 0 && (v1.length !== v2.length)) {
			const v1BiggerThenv2 = v1.length > v2.length;
			const maxLensVersion = v1BiggerThenv2 ? v1 : v2;
			for (let i = minVersionLens; i < maxLensVersion.length; i++) {
				const curVersion = Number(maxLensVersion[i])
				if (curVersion > 0) {
					v1BiggerThenv2 ? result = 1 : result = -1
					break;
				}
			}
		}

		return result;
	}

	export default {
		data() {
			return {
				// 从之前下载安装
				installForBeforeFilePath: '',

				// 安装
				installed: false,
				installing: false,

				// 下载
				downloadSuccess: false,
				downloading: false,

				downLoadPercent: 0,
				downloadedSize: 0,
				packageFileSize: 0,

				tempFilePath: '', // 要安装的本地包地址

				// 默认安装包信息
				title: '更新日志',
				contents: '',
				is_mandatory: false,

				// 可自定义属性
				subTitle: '发现新版本',
				downLoadBtnTextiOS: '立即跳转更新',
				downLoadBtnText: '立即下载更新',
				downLoadingText: '安装包下载中,请稍后'
			}
		},
		onLoad() {
			const localPackageInfo = uni.getStorageSync('PACKAGE_INFO_KEY');

			const requiredKey = ['version', 'url', 'type']
			for (let key in localPackageInfo) {
				if (requiredKey.indexOf(key) !== -1 && !localPackageInfo[key]) {
					console.error(`参数 ${key} 必填,请检查后重试`)
					uni.navigateBack()
					return;
				}
			}

			Object.assign(this, localPackageInfo)
			this.checkLocalStoragePackage()
		},
		onBackPress() {
			// 强制更新不允许返回
			if (this.is_mandatory) {
				return true
			}

			downloadTask && downloadTask.abort()
		},
		onHide() {
			openSchemePromise = null
		},
		computed: {
			isWGT() {
				return this.type === 'wgt'
			},
			// isiOS() {
			// 	return !this.isWGT ? this.platform.includes(platform_iOS) : false;
			// },
			// isAppStore() {
			// 	return this.isiOS || (!this.isiOS && !this.isWGT && this.url.indexOf('.apk') === -1)
			// }
		},
		methods: {
			checkLocalStoragePackage() {
				// 如果已经有下载好的包,则直接提示安装
				const localFilePathRecord = uni.getStorageSync(localFilePathKey)
				if (localFilePathRecord) {
					const {
						version,
						savedFilePath,
						installed
					} = localFilePathRecord

					// 比对版本
					if (!installed && compare(version, this.version) === 0) {
						this.downloadSuccess = true;
						this.installForBeforeFilePath = savedFilePath;
						this.tempFilePath = savedFilePath
					} else {
						// 如果保存的包版本小 或 已安装过,则直接删除
						this.deleteSavedFile(savedFilePath)
					}
				}
			},
			async closeUpdate() {
				if (this.downloading) {
					if (this.is_mandatory) {
						return uni.showToast({
							title: '下载中,请稍后……',
							icon: 'none',
							duration: 500
						})
					}
					uni.showModal({
						title: '是否取消下载?',
						cancelText: '否',
						confirmText: '是',
						success: res => {
							if (res.confirm) {
								downloadTask && downloadTask.abort()
								uni.navigateBack()
							}
						}
					});
					return;
				}

				if (this.downloadSuccess && this.tempFilePath) {
					// 包已经下载完毕,稍后安装,将包保存在本地
					await this.saveFile(this.tempFilePath, this.version)
					uni.navigateBack()
					return;
				}

				uni.navigateBack()
			},
			// closeUp() {
			// 	if (this.downloading) {
			// 		// if (this.is_mandatory) {
			// 		// 	return uni.showToast({
			// 		// 		title: '下载中,请稍后……',
			// 		// 		icon: 'none',
			// 		// 		duration: 500
			// 		// 	})
			// 		// }
			// 		uni.showModal({
			// 			title: '是否取消下载?',
			// 			cancelText: '否',
			// 			confirmText: '是',
			// 			success: res => {
			// 				if (res.confirm) {
			// 					downloadTask && downloadTask.abort()
			// 					let curPage = getCurrentPages();
			// 					let route = curPage[curPage.length - 2].route; //获取当前页面的路由
			// 					if (route == 'pages/login/login') {
			// 						console.log(8888888);
			// 						uni.reLaunch({
			// 							url: '/pages/login/login'
			// 						})
			// 					} else {
			// 						uni.switchTab({
			// 							url: '/' + route
			// 						}); // 返回上一页
			// 					}
			// 				}
			// 			}
			// 		});
			// 		return;
			// 	}

			// 	let curPage = getCurrentPages();
			// 	let route = curPage[curPage.length - 2].route; //获取当前页面的路由
			// 	if (route == 'pages/login/login') {
			// 		console.log(8888888);
			// 		uni.reLaunch({
			// 			url: '/pages/login/login'
			// 		})
			// 	} else {
			// 		uni.switchTab({
			// 			url: '/' + route
			// 		}); // 返回上一页
			// 	}


			// },
			updateApp() {
				this.downloadPackage()
			},
			// 跳转应用商店

			downloadPackage() {
				this.downloading = true;

				//下载包
				downloadTask = uni.downloadFile({
					url: this.url,
					success: res => {
						if (res.statusCode == 200) {
							this.downloadSuccess = true;
							this.tempFilePath = res.tempFilePath

							// 强制更新,直接安装
							if (this.is_mandatory) {
								this.installPackage();
							}
						}
					},
					complete: () => {
						this.downloading = false;

						this.downLoadPercent = 0
						this.downloadedSize = 0
						this.packageFileSize = 0

						downloadTask = null;
					}
				});

				downloadTask.onProgressUpdate(res => {
					this.downLoadPercent = res.progress;
					this.downloadedSize = (res.totalBytesWritten / Math.pow(1024, 2)).toFixed(2);
					this.packageFileSize = (res.totalBytesExpectedToWrite / Math.pow(1024, 2)).toFixed(2);
				});
			},
			installPackage() {
				// #ifdef APP-PLUS
				// wgt资源包安装
				if (this.isWGT) {
					this.installing = true;
				}
				plus.runtime.install(this.tempFilePath, {
					force: false
				}, async res => {
					this.installing = false;
					this.installed = true;

					// wgt包,安装后会提示 安装成功,是否重启
					if (this.isWGT) {
						// 强制更新安装完成重启
						uni.showLoading({
							icon: 'none',
							title: '安装成功,正在重启……'
						})
						setTimeout(() => {
							uni.hideLoading()
							this.restart();
						}, 1000)

					} else {
						const localFilePathRecord = uni.getStorageSync(localFilePathKey)
						uni.setStorageSync(localFilePathKey, {
							...localFilePathRecord,
							installed: true
						})
					}
				}, async err => {
					// 如果是安装之前的包,安装失败后删除之前的包
					if (this.installForBeforeFilePath) {
						await this.deleteSavedFile(this.installForBeforeFilePath)
						this.installForBeforeFilePath = '';
					}

					// 安装失败需要重新下载安装包
					this.installing = false;
					this.installed = false;

					uni.showModal({
						title: '更新失败,请重新下载',
						content: err.message,
						showCancel: false
					});
				});

				// 非wgt包,安装跳出覆盖安装,此处直接返回上一页
				if (!this.isWGT && !this.is_mandatory) {
					uni.navigateBack()
				}
				// #endif
			},
			restart() {
				this.installed = false;
				// #ifdef APP-PLUS
				//更新完重启app
				plus.runtime.restart();
				// #endif
			},
			saveFile(tempFilePath, version) {
				return new Promise((resolve, reject) => {
					uni.saveFile({
						tempFilePath,
						success({
							savedFilePath
						}) {
							uni.setStorageSync(localFilePathKey, {
								version,
								savedFilePath
							})
						},
						complete() {
							resolve()
						}
					})
				})
			},
			deleteSavedFile(filePath) {
				uni.removeStorageSync(localFilePathKey)
				return uni.removeSavedFile({
					filePath
				})
			},

		}
	}
</script>

<style>
	page {
		background: transparent;
	}

	.flex-center {
		/* #ifndef APP-NVUE */
		display: flex;
		/* #endif */
		justify-content: center;
		align-items: center;
	}

	.mask {
		position: fixed;
		left: 0;
		top: 0;
		right: 0;
		bottom: 0;
		background-color: rgba(0, 0, 0, .65);
	}

	.botton-radius {
		border-bottom-left-radius: 30rpx;
		border-bottom-right-radius: 30rpx;
	}

	.content {
		position: relative;
		top: 0;
		width: 600rpx;
		background-color: #fff;
		box-sizing: border-box;
		padding: 0 50rpx;
		font-family: Source Han Sans CN;
	}

	.text {
		/* #ifndef APP-NVUE */
		display: block;
		/* #endif */
		line-height: 200px;
		text-align: center;
		color: #FFFFFF;
	}

	.content-top {
		position: absolute;
		top: -195rpx;
		left: 0;
		width: 600rpx;
		height: 270rpx;
	}

	.content-top-text {
		font-size: 45rpx;
		font-weight: bold;
		color: #F8F8FA;
		position: absolute;
		top: 120rpx;
		left: 50rpx;
		z-index: 1;
	}

	.content-header {
		height: 70rpx;
	}

	.title {
		font-size: 33rpx;
		font-weight: bold;
		color: #3DA7FF;
		line-height: 38px;
	}

	.footer {
		height: 150rpx;
		display: flex;
		align-items: center;
		justify-content: space-around;
	}

	.box-des-scroll {
		box-sizing: border-box;
		padding: 0 40rpx;
		height: 200rpx;
		text-align: left;
	}

	.box-des {
		font-size: 26rpx;
		color: #000000;
		line-height: 50rpx;
	}

	.progress-box {
		width: 100%;
	}

	.progress {
		width: 90%;
		height: 40rpx;
		border-radius: 35px;
	}

	.close-img {
		width: 70rpx;
		height: 70rpx;
		z-index: 1000;
		position: absolute;
		bottom: -120rpx;
		left: calc(50% - 70rpx / 2);
	}

	.content-button {
		text-align: center;
		flex: 1;
		font-size: 30rpx;
		font-weight: 400;
		color: #FFFFFF;
		border-radius: 40rpx;
		margin: 0 18rpx;

		height: 80rpx;
		line-height: 80rpx;

		background: linear-gradient(to right, #1785ff, #3DA7FF);
	}

	.flex-column {
		display: flex;
		flex-direction: column;
		align-items: center;
	}
</style>

如何插入一段漂亮的代码片

博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片.

// An highlighted block
var foo = 'bar';

XML文件格式(新建一个TXT文本复制下面内容更改后缀名为XML)

更改为自己项目的信息

<update>
<package>bao.ming.com</package>
<name>APP</name>
<appid>__UNI__8888683</appid>
<title>标题1.0.99更新</title>
<version>1.0.99</version>
<contents>测试更新内容</contents>
<platform>["Android"]</platform>
<type>["native_app"]</type>
<url>http://www.APP.apk</url> //更改自己的安装包下载地址
</update>

使用方法

在APP.vue里面引入

import upgrade from '@/components/upgrade-center/up.js'

在APP.VUE的onLaunch或则onShow里面使用

onLaunch: function() {
			upgrade()		
		},
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
uniapp是一个基于Vue.js框架的跨平台应用开发框架,可以使用一套代码同时在多个平台上进行开发,例如iOSAndroid和Web等。在uniapp中实现app端的在线升级功能以及下载进度调控需经过以下步骤: 首先,在后端服务器上准备好最新版本的app文件,并提供相应的下载链接。 其次,在uniappApp.vue文件中,监听app的启动事件,通过调用uni.request方法向后端发送请求获取最新版本的信息。 然后,与服务器端返回的版本信息进行比较,判断是否有新版本需要更新。如果有新版本需要更新,将服务器端返回的下载链接存储到本地。 接下来,通过uni.downloadFile方法进行文件下载,同时使用uni.showLoading方法显示下载进度。 在下载过程中,可以通过uni.onDownloadProgress方法监听下载进度变化,并根据下载进度进行相应的界面更新或用户提示。 当下载完成后,使用uni.hideLoading方法隐藏下载进度提示,并提示用户是否立即安装新版本。如果用户选择立即安装,可以通过uni.openDocument方法打开下载的app文件进行安装。 最后,如果用户选择稍后安装或者取消安装,可以在合适的时机再次提醒用户是否需要更新,重复上述步骤。 总之,通过以上步骤,可以实现uniapp app端的在线升级功能以及下载进度调控,让用户可以方便地获取到最新版本的app,并且在下载过程中可以实时查看下载进度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值