uniapp开发打包的app远程升级功能的实现

前言

uniapp官方推荐的都需要使用它自家的云端基于 uniCloud 云函数实现,使用倒是简单了,但是需要依赖它,还得安装管理后台。实际如果有自己的服务器资源的话,把升级文件放到服务器目录,自己去实现app远程升级,也不是什么麻烦事。虽然应用商店也能发布,但是每次都要审核,在局域网搭建自己的升级服务就显得很有必要了。

这里主要针对打包的app升级,小程序的在线升级简单,在微信公众平台就支持。

官方推荐App升级中心 uni-upgrade-center,它提供了 App 的版本更新服务。还可以使用其他插件,如dcloudio/uni-app-plus/lib/update ,DCloud 提供的一个用于应用更新的插件。这个插件主要用于在 UniApp 项目中实现应用的在线更新功能。

观影app体验地址:搜索微信公众号[猫青年] ,回复关键词 “影视” 获取下载链接。

如何实现

1.首先是uniapp 生成apk

用Hbuilder 进行打包,可以选择云打包。也可以从网站https://www.yunedit.com/reg?goto=cert 使用自有证书,目测比直接使用云证书要快一些。

2.实现过程

1.准备update.json文件

准备update.json文件,供前端访问升级信息用。

{
 
  "versionCode": 101,
  "versionName": "v1.0.1",
  "versionDesc": "功能优化",
  "appName": "android_myapp.apk",
  "downloadUrl": "http://111.xx.xx.xx:8000/static/release/"
}

2. 客户端检查更新

在客户端,你可以使用uni.request方法来请求这个JSON文件,并对比当前版本和最新版本。如果发现新版本,提示用户更新。

export default {
    methods: {
        checkForUpdates() {
            const currentVersion = '1.1.0'; // 当前应用版本
            uni.request({
                url: 'https://your-update-server.com/update.json',
                success(res) {
                    const latestVersion = res.data.version;
                    const updateUrl = res.data.url;
                    const updateDescription = res.data.description;

                    if (latestVersion > currentVersion) {
                        uni.showModal({
                            title: '发现新版本',
                            content: updateDescription,
                            confirmText: '立即更新',
                            success(res) {
                                if (res.confirm) {
                                    this.downloadAndInstallUpdate(updateUrl);
                                }
                            }
                        });
                    } else {
                        uni.showToast({
                            title: '已是最新版本',
                            icon: 'none'
                        });
                    }
                },
                fail(err) {
                    console.error('检查更新失败', err);
                }
            });
        },
        downloadAndInstallUpdate(updateUrl) {
            // 下载并安装更新包的逻辑
        }
    },
    onLoad() {
        this.checkForUpdates();
    }
}

3. 下载更新包

下载更新包可以使用uni.downloadFile方法。下载完成后,保存文件到本地。

downloadAndInstallUpdate(updateUrl) {
    const downloadTask = uni.downloadFile({
        url: updateUrl,
        success(res) {
            if (res.statusCode === 200) {
                const filePath = res.tempFilePath;
                this.installUpdate(filePath);
            } else {
                console.error('下载更新包失败');
            }
        },
        fail(err) {
            console.error('下载更新包失败', err);
        }
    });

    downloadTask.onProgressUpdate((res) => {
        console.log('下载进度', res.progress);
        console.log('已经下载的数据长度', res.totalBytesWritten);
        console.log('预期需要下载的数据总长度', res.totalBytesExpectedToWrite);
    });
}

4. 安装更新包

安装更新包的逻辑会根据不同的平台有所不同。以下是Android平台上的示例:

installUpdate(filePath) {
    plus.runtime.install(filePath, {
        force: false,
        success(res) {
            console.log('安装成功');
            plus.runtime.restart();
        },
        fail(err) {
            console.error('安装失败', err);
        }
    });
}

3.详细实现

 index.vue首页界面增加使用个popup弹出组件,用来展示升级进度。

<template>
	<view class="content">
<!-- 升级弹出框 -->
	   <uni-popup ref="popup" type="center" :mask-click="false" catchTouchMove>
			<view class="popup-content">
		        <text>{{percentText}}</text>
			    <progress  :percent="progress" :stroke-width="10" />
		    </view>
	   </uni-popup>
			
	</view>
</template>

<style scoped>

.popup-content {
	  text-align: center;
	  max-width: 500rpx; /* 限定最大高度 */
	  overflow:hidden;
	  background-color: #00aaff;
	  border-radius: 10rpx; 
	  padding: 50px;
	}
</style>

 在checkVersion()方法里,调用接口checkUpdate去请求后台的update.json文件,获取升级信息。

// 检查更新
export const checkUpdate = async (verCode,verName) => {
  try {
	console.log('checkUpdate request');
    const response = await uni.$http.get('/update.json',{verCode:verCode,verName:verName});
	
	console.log(response);
    if (response.statusCode !== 200) {
      uni.showToast({
        title: '数据请求失败! ',
        duration: 1500,
        icon: 'none',
      });
      return [];
    }
    return response.data;
  } catch (error) {
    console.error('Network request failed:', error);
    uni.showToast({
      title: '网络请求失败! ',
      duration: 1500,
      icon: 'none',
    });
    return [];
  }
};

比较版本号,判断是否应该升级。需要升级则使用uni.showModal弹出提示框。

if(verCode < result.versionCode){
						// 更新提醒
						uni.showModal({
							title: '发现新版本,是否更新',
							content: '待更新版本:' + result.versionName +'\n更新说明:'+result.versionDesc,
							success: res => {
								// 点击确定
								if (res.confirm) {
									// 打开手机自带浏览器下载
									//plus.runtime.openURL(result.downloadUrl+result.appName)
									this.downloadUrl = result.downloadUrl+result.appName
									console.log('downloadUrl:'+this.downloadUrl)
									//显示升级进度
									this.showPopup();
									if (this.downloadUrl) {
										this.isStartDownload = true
										//开始下载App
										downloadApp(this.downloadUrl, current => {
											//下载进度
											this.progress = current
										}).then(fileName => {
											//下载完成
											this.isDownloadFinish = true
											this.fileName = fileName
											if (fileName) {
												//自动安装App
												this.handleInstallApp()
											}
										}).catch(e => {
											console.log(e, 'e')
										})
									} else {
										uni.showToast({
											title: '下载链接不存在',
											icon: 'none'
										})
									}
									//this.startProgress();
								}
							}
						});
					}

 完整实现

<script>
	import TodayBoxOffice from '@/components/TodayBoxOffice.vue';
	import { downloadApp,installApp } from '@/utils/upgrade.js';
	
	export default {
		components: {
		    TodayBoxOffice
		  },
		data() {
			return {
				//升级相关
				downloadUrl: '', //APP下载链接
				isDownloadFinish: false, //是否下载完成
				progress: 0,  //升级进度条百分比
				fileName: '', //下载后app本地路径名称
			}
		},
		async onLoad() {
			console.log("onLoad")
			//console.log(this.swiperList)
		},
		computed: {
			//百分比文字
			percentText() {
				let percent = this.progress;
				if (typeof percent !== 'number' || isNaN(percent)) return '正在升级中...'
				if (percent < 100) return `正在升级中... ${percent}%`
				return '下载完成'
			}
		},
        methods: {
			checkVersion(){
				console.log("checkVersion:")
				// 获取本地应用资源版本号
				let version = "1.0.0"
				let verCode = 0
				const systemInfo = uni.getSystemInfoSync();
				// 应用程序版本号
				// #ifdef APP
				version = plus.runtime.version;
				verCode = plus.runtime.versionCode;
				// #endif
				// #ifdef H5
				version = systemInfo.appVersion;
				// #endif
                console.log(version)
				checkUpdate(verCode,version).then(result => {
					console.log("checkUpdate,result:");
					console.log(result);
					if(verCode < result.versionCode){
						// 更新提醒
						uni.showModal({
							title: '发现新版本,是否更新',
							content: '待更新版本:' + result.versionName +'\n更新说明:'+result.versionDesc,
							success: res => {
								// 点击确定
								if (res.confirm) {
									// 打开手机自带浏览器下载
									//plus.runtime.openURL(result.downloadUrl+result.appName)
									this.downloadUrl = result.downloadUrl+result.appName
									console.log('downloadUrl:'+this.downloadUrl)
									//显示升级进度
									this.showPopup();
									if (this.downloadUrl) {
										this.isStartDownload = true
										//开始下载App
										downloadApp(this.downloadUrl, current => {
											//下载进度
											this.progress = current
										}).then(fileName => {
											//下载完成
											this.isDownloadFinish = true
											this.fileName = fileName
											if (fileName) {
												//自动安装App
												this.handleInstallApp()
											}
										}).catch(e => {
											console.log(e, 'e')
										})
									} else {
										uni.showToast({
											title: '下载链接不存在',
											icon: 'none'
										})
									}
									//this.startProgress();
								}
							}
						});
					}
					
				})
			},
			showPopup() {
			    this.$refs.popup.open();
			},
			//安装app
			handleInstallApp() {
				//下载完成才能安装,防止下载过程中点击
				if (this.isDownloadFinish && this.fileName) {
					installApp(this.fileName, () => {
						//安装成功,关闭升级弹窗
						 this.$refs.popup.close();
					})
				}
			}
		},
	}
</script>
/**
 * @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); //注册广播
 
}

其他资源

UNIapp实现局域网内在线升级的操作方法_vue.js_脚本之家

香蕉云编-app打包上架工具类平台

App升级中心 uni-upgrade-center | uniCloud

uni-app实现资源在线升级/热更新【跨平台开发教程uniapp教程(米饭科技-app小程序h5源码)】_uni-app 热更新 平台自带-CSDN博客

https://blog.51cto.com/u_16353097/8783663

uni-app 整包升级/更新方案 - DCloud问答

uniapp实现局域网(内网)中APP自动检测版本,弹窗提醒升级_uni-app 怎么自定义版本更新弹窗-CSDN博客

https://juejin.cn/post/7179990162259050554

hbuilderx+香蕉云编生成打包证书和上架全流程 - 简书

https://www.cnblogs.com/handsome0916/p/17106330.html

在Windows系统上申请iOS证书的详细步骤_windows 申请ios-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

特立独行的猫a

您的鼓励是我的创作动力

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

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

打赏作者

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

抵扣说明:

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

余额充值