uniapp APP端在线升级功能实现讲解——强制或可选升级,下载进度显示

article/2025/9/27 14:54:19

文章目录

概要

需求分析

  

技术实现梳理

 1.是否更新判断:

 2.升级弹窗的展示

 3.根据升级类型限制操作

 4.下载APP监听下载进度

 5.下载完自动安装

核心API讲解

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

 2.plus.runtime.install(安装APP)

        代码实现

     效果演示

        注意事项


概要

本文主要讲述uniapp APP在线升级功能实现,并用代码演示包括强制升级、可选升级、下载进度显示、下载自动安装等功能,示例代码已经过测试可结合实际开发场景做调整直接引入使用

需求分析

要求:

1.打开APP自动检测是否有最新版本,如有弹窗提示下载更新

2.升级类型分为可选更新,强制更新,可选更新用户可以选择关闭不更新情况下继续使用APP,强制更新用户无法关闭更新窗口,无法使用任何功能,必须在线升级后才能使用

3.下载过程进度条显示下载进度

4.下载完成自动跳转安装界面,用户取消安装还能继续手动点击安装

  

技术实现梳理

    1.是否更新判断:

        通过接口获取线上最新版本号(默认规定版本号为正整数)与本地APP版本号进行比较大小,当线上最新版本号大于本地版本号就需要更新。本地App版本可在每次发版时候在manifest.json-基础配置-应用版本号进行设置

   2.升级弹窗的展示

       升级弹窗实现有2种方案,一种直接在首页里嵌套弹窗组件,另一种是把弹窗放置在独立的页面,并把页面窗口设置透明,当需要升级的时候直接从首页进入,从视觉效果上看就相当于在首页上的悬浮窗口。考虑到后续有强制更新页面不能返回等操作,便于维护本案例将采用第二种方案

 3.根据升级类型限制操作

   当升级类型为强制升级意味着页面不能做除了升级的任何操作,包括返回功能,关闭弹窗功能,禁止返回可通过onBackPress生命周期函数处理,弹窗关闭入口动态控制,包括关闭按钮,遮罩层点击关闭功能等

 4.下载APP监听下载进度

  通过H5+方式下载 :plus.downloader.createDownload,生成下载任务对象(downloadTask),通过downloadTask.addEventListener("statechanged",(task,status)=>{})监听下载进度

 5.下载完自动安装

  通过H5+ plus.runtime.install实现自动安装,该api只能监听是否打开安装页面,无法监测到apk是否安装成功,还需要调用安卓原生注册广播事件,监听apk安装成功回调

核心API讲解

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

     说明:

        请求下载管理创建新的下载任务,创建成功则返回Download对象,用于管理下载任务   

     参数:

  • url: ( String ) 必选 要下载文件资源地址

    要下载文件的url地址,仅支持网络资源地址,支持http或https协议。 允许创建多个相同url地址的下载任务。 注意:如果url地址中包含中文或空格等,需要进行urlencode转换。

  • options: DownloadOptions ) 可选 下载任务的参数

    可通过此参数设置下载任务属性,如保存文件路径、下载优先级等。

  • completedCallback: DownloadCompletedCallback ) 可选 下载任务完成回调函数

    当下载任务下载完成时触发,成功或失败都会触发。

    返回值:

     Download:新建的下载任务对象

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

    说明:

    支持以下类型安装包: 1. 应用资源安装包(wgt),扩展名为'.wgt'; 2. 应用资源差量升级包(wgtu),扩展名为'.wgtu'; 3. 系统程序安装包(apk),要求使用当前平台支持的安装包格式。       注意:仅支持本地地址,调用此方法前需把安装包从网络地址或其他位置放置到运行时环境可以访问的本地目录。

   参数:

  • filePath: ( String ) 必选 要安装的文件路径

    支持应用资源安装包(wgt)、应用资源差量升级包(wgtu)、系统程序包(apk)。

  • options: ( WidgetOptions ) 可选

    应用安装设置的参数

  • installSuccessCB: ( InstallSuccessCallback ) 可选

    正确安装后的回调

  • installErrorCB: ( InstallErrorCallback ) 可选

    安装失败的回调

  返回值:

     void : 无

代码实现

    项目目录:

     

 pages.json:

    新增升级弹窗页面路由,设置窗口透明

  {"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:(首页)

  从接口获取最新版本号,跟本地对比,判断是否进入升级弹窗页

<script>export default {data() {return {}},onLoad() {this.init()},methods: {//初始化init() {// #ifdef APP-PLUSthis.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_bg.png自行放入static

<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//开始下载AppdownloadApp(this.downloadUrl, current => {//下载进度监听this.hasProgress = truethis.currentPercent = current}).then(fileName => {//下载完成this.isDownloadFinish = truethis.fileName = fileNameif (fileName) {//自动安装Appthis.handleInstallApp()}}).catch(e => {console.log(e, 'e')})} else {uni.showToast({title: '下载链接不存在',icon: 'none'})}},//安装apphandleInstallApp() {//下载完成才能安装,防止下载过程中点击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(下载、安装工具类):

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

效果演示

注意事项

      1.无法弹出安装APP界面 

          manifest.json-APP常用其他设置-targetSdkVersion必须设置26以上

     2.无法安装APP

         manifest.json-APP权限设置需勾选:

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

    3.获取的版本号和设置的不一致

        通过uni.getSystemInfoSync().appVersionCode获取的本地应用版本号和manifest.json-应用版本号设置不一致,需要云打包或者自定义基座里面才能生效

    4.无法获取下载进度

      app下载请求回复体头部需要返回content-length字段,才能正常获取到app总大小,需要下载接口开启支持,本演示例子已做显示的兼容处理。

 


http://chatgpt.dhexx.cn/article/xvARPMs4.shtml

相关文章

Windows如何一键永远禁止系统更新?

大家好&#xff0c;我是小寻&#xff0c;欢迎关注公众号:工具优选&#xff0c;免费领取优质项目源码和常用工具&#xff0c;还可以加入我的交流群! 一、工具介绍 想必大家也会与小编存在同样的困惑&#xff0c;为啥我电脑上的windows会频繁的强制系统升级&#xff1f;windows…

win10总强制更新?教你永久关闭

win10系统个人觉得还挺好用的&#xff0c;但是有一点非常烦人&#xff0c;就是隔三岔五强制自动更新&#xff01; 相信也是大部分用户最不喜欢的一点。 更新后&#xff0c;系统可能还会出现一些bug&#xff0c;而且每次更新都要等上一段时间。对于每天工作繁忙的用户来说&…

解决Xshell/Xftp强制升级无法进入问题

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 文章目录 前言 一、首先创建一个Xshell.txt文件&#xff0c;用notepad打开 二、将以下代码复制入.txt文件 1.实例代码 2.保存文件&#xff0c;重命名 3.以管理员身份运行…

杰理强制升级工具4.0使用和原理解析

用那个有8个挡位的烧录工具&#xff08;4.0工具&#xff09;的话&#xff0c;默认是走USB的&#xff0c;不是走串口&#xff0c;工具的DP接芯片的DP&#xff0c;工具的DM接芯片的DM&#xff0c;工具的5V接芯片的VBAT&#xff08;要保证能控制芯片供电通断才能从mask启动&#x…

无视Win11 TPM/英特尔芯片等配置,强制升级Win11

上次我写了一个DEV强制升级的文章&#xff0c;可是DEV版本的Windows11系统很不稳定&#xff0c;软件可能无法正常使用。这篇文以适用于任何电脑。建议看这篇文章哦&#xff01; 1.下载映像 大家可以在网上找.iso映像&#xff0c;也可以点击此处。 在网站上找到[下载 Windows…

Android版本强制更新

目前的项目之中基本上都会存在版本更新的功能&#xff0c;分为强制更新和推荐更新&#xff0c;其实功能点都是一样的&#xff0c;推荐更新只是增加一个按钮让更新的弹框隐藏掉而已&#xff0c;这里仅记录强制更新的功能首先需要跟接口约定&#xff0c;需要判断是否弹出更新弹框…

强制更新客html页面,强制更新

强制更新 1. 什么是强制更新 当某个qp包希望用户快速下载到时&#xff0c;可以使用强制更新。 强制更新是为了解决出了故障或者希望某个版本(业务做活动)的QP 包能快速被更新到而设计的功能&#xff0c;使用起来也相当简单&#xff0c;只需要在发布QP 包的时候&#xff0c;选中…

微软将开启PC Win10 20H2正式版强制升级

本文转载自IT之家&#xff0c;IT之家3月4日消息 外媒 Windows Latest 报道&#xff0c;在未来几周或几个月内&#xff0c;微软表示将开始在运行过时版本操作系统的设备上安装 Windows 10 20H2 版本 “2020 年 10 月更新”。微软显然是在使用 “机器学习”来自动升级兼容硬件&am…

实战:去除未加固 Android App强制升级提醒

去除一个未加固APP的升级提示弹窗&#xff0c;我们先看看app 是否进行加壳&#xff0c;发现使用的是邦邦免费加壳 我们在测试机上安装这个APP&#xff0c;安装命令如下&#xff1a; adb installl C:\Users\Avenue\Desktop\AndroidFridaBeginnersBook-main\Chap05\com.hello.…

一步强制升级windows11

许多人想升级windows11&#xff0c;可是配置不允许啊 今天教大家一种方法绕过安全启动和TPM强制升级win11 首先&#xff0c;在微软官方网站下载正版win11镜像 https://www.microsoft.com/zh-cn/software-download/windows11/ 建议选择multi-edition版 这个版本是多版本混合…

OBS 实现强制升级功能

一&#xff0c;要实现的效果 版本更新后发布最新的OBS版本 到阿里云用户端打开OBS时&#xff0c;强制让用户从阿里云更新最新版本 二&#xff0c;OBS升级 底层逻辑 三&#xff0c;修改的地方 win-update.cpp //升级 线程 void AutoUpdateThread::run() try {long …

绕过限制,强制升级至Windows11

目录 以前…… 开始升级。 新界面预览 附&#xff1a;文档下载 注意:本教程使用Windows11 insider preview,可能会有系统崩溃等现象。如需安装普通版本,请参考此处。 以前…… 以前的强制升级是这样的&#xff1a; 点击此链接查看详情&#xff1a;http://www.pccppc.com/…

真我GT realme GT root权限 解锁BL 刷ROOT教程 免费 TWRP

realme GT root后变砖了 也可以联系技术远程救砖 http://www.aialbb.com/thread-1443-1-1.html 可以9008 刷机 开机 解决不开机问题 realme GT解锁及ROOT教程 刷机有风险 入门需谨慎 仅限GT 文件下载总链接查看链接 机型:realme真我GT 代号:RMX2202 地区:中国CN A.解锁BL锁 1…

谷歌系列手机刷机和面具ROOT教程

目录 一、环境介绍二、准备工具2.1 下载官方ROM2.2 下载Magisk文件2.3 下载TWRP文件 三、刷机四、安装面具五、总结 一、环境介绍 1.一台真机&#xff1a;本文使用的是pixel 1代 2.adb环境的配置&#xff1a;安装Android Stadio(AS)后&#xff0c;都会配置。 3.需要安装的镜像(…

Ubuntu的root

Ubuntu的root 介绍给root用户设置密码并使用引用 介绍 安装ubuntu成功后&#xff0c;都是普通用户权限&#xff0c;并没有最高root权限&#xff0c;如果需要使用root权限的时候&#xff0c;通常都会在命令前面加上 sudo。有的时候感觉很麻烦。 我们一般使用su命令来直接切换到…

一加6T手机Android10 root教程

一加六T手机Android10 root教程 刷机之前一定要备份&#xff01;备份&#xff01;备份&#xff01; 第一步&#xff1a;准备文件第二步&#xff1a;准备工作第三步&#xff1a;解BL锁第四步&#xff1a;刷入第三方TWRP第五步&#xff1a;刷入Magisk 前言&#xff1a;一加6T手机…

root全攻略(root是什么 怎么root root能干什么)

首先讲什么是root 为了手机安全 我们是无法对手机系统进行修改 为的是防止用户的误操作比如直接把system删了 导致手机废掉 取得root就是取得android手机的最高控制权 很类似塞班的xx&#xff08;破解&#xff09; root好处 有了最高控制权 就可以修改手机所有地方 只要你有能力…

root手机教程

以前的root基本是su root&#xff0c;但是su不再维护更新之后&#xff0c;就有了现在的比su更好玩的root手机的工具→magisk。Magisk具备很强大的模块扩展&#xff0c;可以不修改system区的文件&#xff0c;达到修改system文件一样的效果&#xff0c;例如换字体&#xff0c;换主…

用安卓手机三步获取root权限教程

因为我有时候装一些软件需要手机的Root权限&#xff0c;而现在一些手机获取有非常的的麻烦&#xff0c;网上查怎样获取的时候各种广告各种坑&#xff0c;下载什么什么一件获得root权限的APP一般都是打广告的流氓软件&#xff0c;安装装后还给你装其它一大堆软件&#xff0c;也根…

Android 系统root教程-magisk最新版

Android 系统root教程-magisk最新版 前言 Magisk之前的root方式都需要通过twrp去刷入文件&#xff0c;而且还有Magisk&#xff08;核心组件&#xff09;和Magisk Manager&#xff08;配套应用程序&#xff09;分开安装&#xff0c;非常麻烦&#xff0c;在Magisk v22.0之后&am…