uniapp实现app自动升级或更新

一、前提:

1.直接使用uniapp官方App升级中心 uni-upgrade-center | uniCloud 方法没有实现app自动升级

2.需要后端配合,用接口返回一个更新的版本号和app下载地址,所以每次更新版本后,要把新的版本号和地址给到后端(注:也可直接把更新之后打包的apk给到后端,让后端发布一个在线下载地址)

二、效果图

打开app时,获取当前app版本号,当当前版本号与后端返回的版本号不同时,跳转到更新页面,点击立即更新后会自动下载

三、存储app在线地址

(1)登录 uniCloud控制台 

 (2)新建一个服务空间

(3)阿里云和支付宝云都有免费版

(4)点击服务空间列表一条,左侧菜单点击空间内置存储

(5)上传打包好的apk文件

(6)上传成功后。点击下载测试是否可以下载

(7)点击详情便可查看下载地址,把这个地址给到后端就可以了

(4)前端代码

(1)首页判断是否更新代码

const systemInfo = uni.getSystemInfoSync();
const current = systemInfo.appWgtVersion;  // 获取app当前版本号

// 跳转到更新页面
if (res.code == 200 && res.data) {
    let obj = {
	    contents: data.latestVersion,
	    is_mandatory: true,
	    url: data.downloadUrl
    }
    uni.navigateTo({
	    url: '/pages/myPackage/pages/upgrade/index?obj='+encodeURIComponent(JSON.stringify(obj))        
    }
})

(2)更新页面代码

  • index.vue 
<template>
	<view class="upgrade-popup">
		<!-- <image class="header-bg" src="../../static/upgrade_bg.png" mode="widthFix"></image> -->
		<view class="main">
			<view class="version">发现新版本{{ versionName }}</view>
			<view class="content">
				<text class="title">更新内容</text>
				<view class="desc" v-html="versionDesc"></view>
			</view>
			<!--下载状态-进度条显示 -->
			<view class="footer" v-if="isStartDownload">
				<view class="progress-view" :class="{ 'active':!hasProgress }" @click="handleInstallApp">
					<!-- 进度条 -->
					<view v-if="hasProgress" style="height: 100%;">
						<view class="txt">{{ percentText }}</view>
						<view class="progress" :style="setProStyle"></view>
					</view>
					<view v-else>
						<view class="btn upgrade force">{{ isDownloadFinish ? '立即安装' : '下载中...' }}</view>
					</view>
				</view>
			</view>
			<!-- 强制更新 -->
			<view class="footer" v-else-if="isForceUpdate">
				<view class="btn upgrade force" @click="handleUpgrade">立即更新</view>
			</view>
			<!-- 可选择更新 -->
			<view class="footer" v-else>
				<view class="btn close" @click="handleClose">以后再说</view>
				<view class="btn upgrade" @click="handleUpgrade">立即更新</view>
			</view>
		</view>
	</view>
</template>

<script setup>
	import {
		ref,
		computed
	} from 'vue';
	import {
		downloadApp,
		installApp
	} from './upgrade.js';
	import {
		onLoad,
		onBackPress
	} from "@dcloudio/uni-app";

	// 响应式数据
	const isForceUpdate = ref(false); // 是否强制更新
	const versionName = ref(''); // 版本名称
	const versionDesc = ref(''); // 更新说明
	const downloadUrl = ref(''); // APP 下载链接
	const isDownloadFinish = ref(false); // 是否下载完成
	const hasProgress = ref(false); // 是否能显示进度条
	const currentPercent = ref(0); // 当前下载百分比
	const isStartDownload = ref(false); // 是否开始下载
	const fileName = ref(''); // 下载后 app 本地路径名称

	// 计算属性
	const setProStyle = computed(() => {
		return {
			width: (510 * currentPercent.value / 100) + 'rpx' // 510:按钮进度条宽度
		};
	});

	const percentText = computed(() => {
		let percent = currentPercent.value;
		if (typeof percent !== 'number' || isNaN(percent)) return '下载中...';
		if (percent < 100) return `下载中${percent}%`;
		return '立即安装';
	});

	// 生命周期钩子
	onLoad((option) => {
		const obj = JSON.parse(decodeURIComponent(option.obj));
		versionName.value = obj.contents; // 版本名称
		versionDesc.value = "修复若干bug"; // 更新说明
		downloadUrl.value = obj.url; // 下载链接
		isForceUpdate.value = true;
	});

	onBackPress((options) => {
		// 禁用返回
		if (options.from === 'backbutton') {
			return true;
		}
	});

	// 方法
	const handleUpgrade = () => {
		if (downloadUrl.value) {
			isStartDownload.value = true;
			// 开始下载 App
			downloadApp(downloadUrl.value, (current) => {
				// 下载进度监听
				hasProgress.value = true;
				currentPercent.value = current;
			}).then((newFileName) => {
				// 下载完成
				isDownloadFinish.value = true;
				fileName.value = newFileName;
				if (newFileName) {
					// 自动安装 App
					handleInstallApp();
				}
			}).catch((e) => {
				console.log(e, 'e');
			});
		} else {
			uni.showToast({
				title: '下载链接不存在',
				icon: 'none'
			});
		}
	};

	const handleInstallApp = () => {
		// 下载完成才能安装,防止下载过程中点击
		if (isDownloadFinish.value && fileName.value) {
			installApp(fileName.value, () => {
				// 安装成功,关闭升级弹窗
				uni.navigateBack();
			});
		}
	};

	const handleClose = () => {
		uni.navigateBack();
	};
</script>

<style>
	page {
		background: rgba(0, 0, 0, 0.5);
		/**设置窗口背景半透明*/
	}
</style>

<style lang="scss" scoped>
	.upgrade-popup {
		width: 580rpx;
		height: auto;
		position: fixed;
		top: 50%;
		left: 50%;
		transform: translate(-50%, -50%);
		background: #fff;
		border-radius: 20rpx;
		box-sizing: border-box;
		border: 1px solid #eee;
	}

	.header-bg {
		width: 100%;
		margin-top: -112rpx;
	}

	.main {
		padding: 10rpx 30rpx 30rpx;
		box-sizing: border-box;

		.version {
			font-size: 36rpx;
			color: #026DF7;
			font-weight: 700;
			width: 100%;
			text-align: center;
			overflow: hidden;
			text-overflow: ellipsis;
			white-space: nowrap;
			letter-spacing: 1px;
		}

		.content {
			margin-top: 60rpx;

			.title {
				font-size: 28rpx;
				font-weight: 700;
				color: #000000;
			}

			.desc {
				box-sizing: border-box;
				margin-top: 20rpx;
				font-size: 28rpx;
				color: #6A6A6A;
				max-height: 80vh;
				overflow-y: auto;
			}
		}

		.footer {
			width: 100%;
			display: flex;
			justify-content: center;
			align-items: center;
			position: relative;
			flex-shrink: 0;
			margin-top: 100rpx;

			.btn {
				width: 246rpx;
				display: flex;
				justify-content: center;
				align-items: center;
				position: relative;
				z-index: 999;
				height: 96rpx;
				box-sizing: border-box;
				font-size: 32rpx;
				border-radius: 10rpx;
				letter-spacing: 2rpx;

				&.force {
					width: 500rpx;
				}

				&.close {
					border: 1px solid #E0E0E0;
					margin-right: 25rpx;
					color: #000;
				}

				&.upgrade {
					background-color: #026DF7;
					color: white;
				}
			}

			.progress-view {
				width: 510rpx;
				height: 90rpx;
				display: flex;
				position: relative;
				align-items: center;
				border-radius: 6rpx;
				background-color: #dcdcdc;
				display: flex;
				justify-content: flex-start;
				padding: 0px;
				box-sizing: border-box;
				border: none;
				overflow: hidden;

				&.active {
					background-color: #026DF7;
				}

				.progress {
					height: 100%;
					background-color: #026DF7;
					padding: 0px;
					box-sizing: border-box;
					border: none;
					border-top-left-radius: 10rpx;
					border-bottom-left-radius: 10rpx;
				}

				.txt {
					font-size: 28rpx;
					position: absolute;
					top: 50%;
					left: 50%;
					transform: translate(-50%, -50%);
					color: #fff;
				}
			}
		}
	}
</style>
  •  upgrade.js
/**
 * @description H5+下载App
 * @param downloadUrl:App下载链接
 * @param progressCallBack:下载进度回调
 */
export const downloadApp = (downloadUrl, progressCallBack = () => {}, ) => {
	return new Promise((resolve, reject) => {
		//创建下载任务
		const downloadTask = plus.downloader.createDownload(downloadUrl, {
			method: "GET"
		}, (task, status) => {
			console.log(status, 'status')
			if (status == 200) { //下载成功
				resolve(task.filename)

			} else {
				reject('fail')
				uni.showToast({
					title: '下载失败',
					duration: 1500,
					icon: "none"
				});
			}
		})
		//监听下载过程
		downloadTask.addEventListener("statechanged", (task, status) => {
			switch (task.state) {
				case 1: // 开始  
					break;
				case 2: //已连接到服务器  
					break;
				case 3: // 已接收到数据  
					let hasProgress = task.totalSize && task.totalSize > 0 //是否能获取到App大小
					if (hasProgress) {
						let current = parseInt(100 * task.downloadedSize / task
						.totalSize); //获取下载进度百分比
						progressCallBack(current)
					}
					break;
				case 4: // 下载完成       
					break;
			}
		});
		//开始执行下载
		downloadTask.start();
	})


}
/**
 * @description H5+安装APP
 * @param fileName:app文件名
 * @param callBack:安装成功回调
 */
export const installApp = (fileName, callBack = () => {}) => {
	//注册广播监听app安装情况
	onInstallListening(callBack);
	//开始安装
	plus.runtime.install(plus.io.convertLocalFileSystemURL(fileName), {}, () => {
		//成功跳转到安装界面
	}, function(error) {
		uni.showToast({
			title: '安装失败',
			duration: 1500,
			icon: "none"
		});
	})

}
/**
 * @description 注册广播监听APP是否安装成功
 * @param callBack:安装成功回调函数
 */
const onInstallListening = (callBack = () => {}) => {

	let mainActivity = plus.android.runtimeMainActivity(); //获取activity
	//生成广播接收器
	let receiver = plus.android.implements('io.dcloud.android.content.BroadcastReceiver', {
		onReceive: (context, intent) => { //接收广播回调  
			plus.android.importClass(intent);
			mainActivity.unregisterReceiver(receiver); //取消监听
			callBack()
		}
	});
	let IntentFilter = plus.android.importClass('android.content.IntentFilter');
	let Intent = plus.android.importClass('android.content.Intent');
	let filter = new IntentFilter();
	filter.addAction(Intent.ACTION_PACKAGE_ADDED); //监听APP安装     
	filter.addDataScheme("package");
	mainActivity.registerReceiver(receiver, filter); //注册广播

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小满blue

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值