Explanation on the implementation of the online upgrade function of the uniapp APP-mandatory or optional upgrade, download progress display

Article directory

overview

demand analysis

  

Technical realization combing

 1. Whether to update the judgment:

 2. Upgrade the display of the pop-up window

 3. Restrict operations based on upgrade type

 4. Download the APP to monitor the download progress

 5. Automatic installation after downloading

Core API Explanation

 1.plus.downloader.createDownload(url,options,completedCallback)(下载)

 2.plus.runtime.install (install APP)

        Code

     Effect demo

        Precautions


overview

This article mainly describes the implementation of the uniapp APP online upgrade function, and uses code to demonstrate functions including mandatory upgrade, optional upgrade, download progress display, download automatic installation, etc. The sample code has been tested and can be directly introduced and used in combination with actual development scenarios.

demand analysis

Require:

1. Open the APP to automatically detect whether there is the latest version, if there is a pop-up window prompting to download the update

2. The upgrade type is divided into optional update and mandatory update. Users of optional update can choose to close and continue to use the APP without updating. Users of mandatory update cannot close the update window and cannot use any functions. They must be upgraded online before they can be used.

3. The progress bar of the download process shows the download progress

4. After the download is completed, it will automatically jump to the installation interface. If the user cancels the installation, he can continue to manually click the installation

  

Technical realization combing

    1. Whether to update the judgment:

        Obtain the latest online version number through the interface (the default version number is a positive integer) and compare it with the local APP version number. When the latest online version number is greater than the local version number, it needs to be updated. The local App version can be set in manifest.json-basic configuration-app version number every time the version is released

   2. Upgrade the display of the pop-up window

       There are two ways to upgrade the pop-up window, one is to directly nest the pop-up window component in the home page, and the other is to place the pop-up window on an independent page, and set the page window to be transparent, and enter directly from the home page when an upgrade is required , visually, it is equivalent to a floating window on the home page. Considering that there will be subsequent operations such as forced update of the page that cannot be returned, the second solution will be adopted in this case for ease of maintenance

 3. Restrict operations based on upgrade type

   When the upgrade type is forced upgrade, it means that the page cannot perform any operations other than the upgrade, including the return function, closing the pop-up window function, and the prohibition of returning can be processed through the onBackPress lifecycle function, and the dynamic control of the pop-up window closing entry, including the close button and the mask layer Click to close function etc.

 4. Download the APP to monitor the download progress

  Download via H5+: plus.downloader.createDownload, generate a download task object (downloadTask), and monitor the download progress via downloadTask.addEventListener("statechanged",(task,status)=>{})

 5. Automatic installation after downloading

  Automatic installation is realized through H5+ plus.runtime.install. This api can only monitor whether the installation page is opened, but cannot monitor whether the apk is installed successfully. It also needs to call the Android native registration broadcast event to monitor the apk installation success callback

Core API Explanation

  1.plus.downloader.createDownload(url,options,completedCallback)(下载)

     illustrate:

        Request download management to create a new download task. If the creation is successful, a Download object will be returned to manage the download task.   

     parameter:

  • url:  ( String ) Mandatory  The resource address of the file to be downloaded

    The url address of the file to be downloaded, only supports network resource addresses, supports http or https protocol. Allows to create multiple download tasks with the same url address. Note: If the url address contains Chinese or spaces, etc., urlencode conversion is required.

  • options:  DownloadOptions  ) Optional  download task parameters

    You can use this parameter to set download task attributes, such as save file path, download priority, etc.

  • completedCallback:  DownloadCompletedCallback  ) Optional  download task completion callback function

    Triggered when the download task download is completed, whether it succeeds or fails.

    return value:

     Download: Newly created download task object

 2.plus.runtime.install(filePath,options,installSuccessCB,installErrorCB)(安装APP)

    illustrate:

    The following types of installation packages are supported: 1. Application resource installation package (wgt), the extension is '.wgt'; 2. Application resource differential upgrade package (wgtu), the extension is '.wgtu'; 3. System program installation package (apk), requires the use of the installation package format supported by the current platform. Note: Only local addresses are supported. Before calling this method, the installation package needs to be placed from a network address or other location to a local directory that the runtime environment can access.

   parameter:

  • filePath:  ( String ) Required  file path to install

    Support application resource installation package (wgt), application resource difference upgrade package (wgtu), system program package (apk).

  • options:  WidgetOptions  ) optional

    Parameters for applying installation settings

  • installSuccessCB: InstallSuccessCallback ) 可选

    Callback after correct installation

  • installErrorCB:  InstallErrorCallback  ) optional

    Callback for installation failure

  return value:

     void  : none

Code

    Project directory:

     

 pages.json:

    New upgrade pop-up window page routing, set the window to be transparent

  {
	"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
		{
			"path": "pages/index/index",//首页
			"style": {
				"navigationBarTitleText": ""
			}
		},
		{
			"path": "pages/index/upgrade",//升级窗口页面
			"style": {
				"navigationBarTitleText": "",
				"navigationStyle": "custom",//导航栏自定义
				"app-plus": {
					"bounce": "none",
                    "animationType":"none",//取消窗口动画
					"background": "transparent" // 设置背景透明
				}
			}
		}
	],
	"globalStyle": {
		"navigationBarTextStyle": "black",
		"navigationBarTitleText": "uni-app",
		"navigationBarBackgroundColor": "#F8F8F8",
		"backgroundColor": "#F8F8F8"
	},
	"uniIdRouter": {}
}

index.vue: (homepage)

  Obtain the latest version number from the interface, compare it with the local version, and judge whether to enter the upgrade pop-up page

<script>
	export default {
		data() {
			return {}
		},
		onLoad() {
			this.init()

		},
		methods: {
			//初始化
			init() {
				// #ifdef APP-PLUS
				this.checkVersion()
				// #endif
			},
			//检查版本更新情况
			checkVersion() {
				//模拟接口获取最新版本号,版本号固定为整数
				setTimeout(() => {
					const newVersionName = "V1.2.0" //线上最新版本名
					const newVersionCode = 20; //线上最新版本号
					const selfVersionCode = Number(uni.getSystemInfoSync().appVersionCode) //当前App版本号

					//线上版本号高于当前,进行在线升级
					if (selfVersionCode < newVersionCode) {
						let platform = uni.getSystemInfoSync().platform //手机平台
						//安卓手机弹窗升级
						if (platform === 'android') {
							uni.navigateTo({
								url: './upgrade'
							})
						}
						//IOS无法在线升级提示到商店下载
						else {
							uni.showModal({
								title: '发现新版本 ' + newVersionName,
								content: '请到App store进行升级',
								showCancel: false
							})
						}
					}


				}, 200)
			}
		}
	}
</script>

upgrade.vue (upgrade popup page):

The layout style can be adjusted according to the actual situation, and the top background image upgrade_bg.png is placed in static by itself

<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>
	import {
		downloadApp,
		installApp
	} from './upgrade.js'
	export default {
		data() {
			return {
				isForceUpdate: false, //是否强制更新
				versionName: '', //版本名称
				versionDesc: '', //更新说明
				downloadUrl: '', //APP下载链接
				isDownloadFinish: false, //是否下载完成
				hasProgress: false, //是否能显示进度条
				currentPercent: 0, //当前下载百分比
				isStartDownload: false, //是否开始下载
				fileName: '', //下载后app本地路径名称
			}
		},
		computed: {
			//设置进度条样式,实时更新进度位置
			setProStyle() {
				return {
					width: (510 * this.currentPercent / 100) + 'rpx' //510:按钮进度条宽度
				}
			},
			//百分比文字
			percentText() {
				let percent = this.currentPercent;
				if (typeof percent !== 'number' || isNaN(percent)) return '下载中...'
				if (percent < 100) return `下载中${percent}%`
				return '立即安装'

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

		},
		methods: {
			//初始化获取最新APP版本信息
			init() {
				//模拟接口获取
				setTimeout(() => {
                   //演示数据请根据实际修改
					this.versionName = 'V1.2.0'; //版本名称
					this.versionDesc = "修复若干bug"; //更新说明
					this.downloadUrl = 'https://xxxxx'; //下载链接
					this.isForceUpdate = false; //是否强制更新
				}, 200)
			},
			//更新
			handleUpgrade() {
				if (this.downloadUrl) {
					this.isStartDownload = true
					//开始下载App
					downloadApp(this.downloadUrl, current => {
						//下载进度监听
						this.hasProgress = true
						this.currentPercent = 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'
					})
				}

			},
			//安装app
			handleInstallApp() {
				//下载完成才能安装,防止下载过程中点击
				if (this.isDownloadFinish && this.fileName) {
					installApp(this.fileName, () => {
						//安装成功,关闭升级弹窗
						uni.navigateBack()
					})
				}
			},
			//关闭返回
			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 (download, install tools):

/**
 * @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); //注册广播

}

Effect demo

Precautions

      1. Unable to pop up the APP installation interface 

         manifest.json-APP commonly used other settings-targetSdkVersion must be set to 26 or above

     2. Unable to install APP

         manifest.json-APP permission settings need to be checked:

        "<uses-permission android:name=\"android.permission.INSTALL_PACKAGES\"/>",
       "<uses-permission android:name=\"android.permission.REQUEST_INSTALL_PACKAGES\"/>"

    3. The obtained version number is inconsistent with the set one

       The local application version number obtained through uni.getSystemInfoSync().appVersionCode is inconsistent with the manifest.json-application version number setting, and it needs to be packaged in the cloud or in a custom base to take effect

    4. Unable to get download progress

      The header of the reply body of the app download request needs to return the content-length field, so as to obtain the total size of the app normally, the support needs to be enabled on the download interface. This demonstration example has been processed for compatibility.

 

Guess you like

Origin blog.csdn.net/sd1sd2/article/details/131241609