uniapp实现应用内检测版本更新(Android直接下载/ios跳转app store)

news2024/11/26 14:46:20
背景:最近需要给app加一个可以检测到新版本并更新的功能,
之前没有考虑过这个问题,第一次尝试,特此记录一下。
我在这里使用到了uniapp上的更新插件,并在此插件基础上进行更改以适应我的项目。
插件链接:https://ext.dcloud.net.cn/plugin?id=2144,感谢大哥!

一、思考

想法:我这里的思路是app发送请求将app版本号发往后端服务器,与服务器上的apk版本号进行比对(也可以从服务器获取到最新版本的版本号返回到前端进行比对,这里就是仁者见仁了),如果有新版本会返回最新版本的url下载地址,在ios中则是跳到app store上进行下载。

二、操作

所需的操作:
要实现这个检测更新的功能,我们肯定是需要在应用启动的时候就运行,
所以我们就需要在App.vue中设置内容,然后把我们更新相关的操作可以
放在一个单独的js文件中;后端方面可以写一个接口文件里面写版本更新
相关的逻辑,还需要有一个/apk目录存放新版本安装包,以供安卓用户直
接从这里下载,实现应用内的下载安装。所以说,前端有两个文件,后端
有两个文件,我们下面就是围绕这几个文件描述。

2.1、App.vue

        这个文件中需要新增的代码就在下面

import checkappupdate from 'js_sdk/wonyes-checkappupdate/wonyes/checkappupdate.js'


checkappupdate.check({
			    title:"检测到有新版本!",
			    content:"请升级app到最新版本!",
			    canceltext:"暂不升级",
			    oktext:"立即升级",
			    api:'https://xxx.xxxx.xxx/api/Update/renew',
			    barbackground:"rgba(50,50,50,0.8)",//进度条背景色,默认灰色,可自定义rgba形式色值
			    barbackgroundactive:"rgba(32,165,58,1)"//进度条前景色色,默认绿色,可自定义rgba形式色值
			})

        注意:上面的导入时我在使用插件时的目录,如果这个js文件你要单独新建的话,记得把这里的路径修改一下,能访问到js文件就行。下面的这个check方法的使用需要放在app.vue的onLaunch里,在api那里xxx.xxxx.xxx是你的域名,这个URL实际上就是访问后端更新相关逻辑接口的URL,为了保持一致,你可以根据自己的项目接口的访问规则自行修改即可。 

2.2、checkappupdate.js

        这个文件就是前端我们发送请求,接受返回数据,判断是否更新,更新提示框,下载apk等存放相关内容的js文件,使用插件的话的也是直接就有的,自己创建的话也可以,我在下方放上我的代码以供参考,但是无论是使用插件还是使用我的,记得修改一些内容已适配自己的逻辑需求。

function check(param = {}) {
	console.log("开始检查版本更新!");
	// 合并默认参数
	param = Object.assign({
		title: "检测到有新版本!",
		content: "请升级app到最新版本!",
		canceltext: "暂不升级",
		oktext: "立即升级",
		barbackground:"rgba(50,50,50,0.8)",
		barbackgroundactive:"rgba(32,165,58,1)"
	}, param)
	if (!param.api) {
		console.log("api有误!");
		return false;
	}
	
	plus.runtime.getProperty(plus.runtime.appid, (widgetInfo) => {
		// console.log(widgetInfo)
		let platform = plus.os.name.toLocaleLowerCase()
		uni.request({
			url: param['api'],
			data: {
				platform: platform,
				version: widgetInfo.version
			},
			header: {
				'content-type': 'application/x-www-form-urlencoded'
			},
			method: 'POST',
			dataType: 'json',
			success: (result) => {
				console.log("检测更新,打印后端返回结果:");
				console.log(result);
				let data = result.data ? result.data : null			

				if (data && data.code === 0 && data.url) {
					if(/\.wgt$/i.test(data.url) || (platform == 'android' &&  /\.apk$/i.test(data.url))){
						// 如果是热更新  wgt 或 android平台下apk
						startdownload(param,data);
						return
					}
					if (platform == 'ios') {
						// 如果是ios,则跳转到appstore
						uni.showModal({
							title: param.title,
							content: data.log ? data.log : param.content,
							showCancel: data.force ? false : true,
							confirmText: param.oktext,
							cancelText: param.canceltext,
							success: res => {
								if (!res.confirm) {
									console.log('取消了升级');
									return
								}else{
									plus.runtime.openURL(result.data.url)
								}
							}
						});
						
						
					}
				}
			},
			fail: (res) => {
				console.log("请求发送失败!");
				console.log(res);
			}
		})
	});
}

function startdownload(param,data){
	uni.showModal({
		title: param.title,
		content: data.log ? data.log : param.content,
		showCancel: data.force ? false : true,
		confirmText: param.oktext,
		cancelText: param.canceltext,
		success: res => {
			if (!res.confirm) {
				console.log('取消了升级');
				return
			}
			if (data.shichang === 1 && /\.apk$/i.test(data.url)) {
				//去应用市场更新
				plus.runtime.openURL(data.shichangurl);
				plus.runtime.restart();
			} else {
				// 开始下载
				// 创建下载任务
				var dtask = plus.downloader.createDownload(data.url, {
						filename: "_downloads/"
					},
					function(d, status) {
						console.log('d',d)
						// 下载完成
						if (status == 200) {
							plus.runtime.install(d.filename, {
								force: false
							}, function() {
								//进行重新启动;
								plus.runtime.restart();
							}, (e) => {
								uni.showToast({
									title: '安装升级包失败:' + JSON.stringify(e),
									icon: 'none'
								})
							});
						} else {
							plus.nativeUI.alert("下载升级包失败,请手动去站点下载安装,错误码: " + status);
						}
				});
				
				let wrapwidth=parseInt(plus.screen.resolutionWidth / 2);
				let view = new plus.nativeObj.View("maskView", {
					backgroundColor: param.barbackground,
					left: (wrapwidth/2) + "px",
					bottom: "80px",
					width: wrapwidth+"px",
					height: "10px"
				});
				
				view.show()
				let viewinner = new plus.nativeObj.View("maskViewinner", {
					backgroundColor: param.barbackgroundactive,
					left: (wrapwidth/2)+"px",
					bottom: "80px",
					width: "1px",
					height: "10px"
				});
				viewinner.show();
				dtask.addEventListener("statechanged", (e) => {
					if (e && e.downloadedSize > 0) {
						let jindu = parseInt((e.downloadedSize / e.totalSize)*wrapwidth)
						viewinner.setStyle({width:jindu+'px'});
					}
				}, false);
				dtask.start();
			}
		}
	});
}

export default {
	check
}

        注意:在这个代码的操作下更新的进度条是在页面下方,不太明显,我试着不太好调也就没有改动,这里可根据自己情况自行更改,代码是let wrapwidth这里往下。

2.3、后端接口Update.php 

        这里就是后端接口了,还是那句话,插件和我的不一定合适,只供参考作用,需要根据自己实际的项目逻辑自行修改适配。

<?php
namespace app\api\controller;
		
use app\common\controller\Api;
class Update extends Api
{
	protected $noNeedLogin = ['renew'];
	protected $noNeedRight = ['*'];
	
	// 判断2个版本号的大小,并返回较大的那个,比如 1.0.9  1.1.0 ,则返回 1.1.0
	// 传入由2个点分隔为3组的版本号,返回较大的那个
	// 1.0.3  1.2.9 等
	function getbig($one,$two){
	    $onearr=explode('.',$one);
	    $twoarr=explode('.',$two);
	    if(intval($onearr[0]) !== intval($twoarr[0])){
	        return intval($onearr[0])>intval($twoarr[0])?$one:$two;
	    }
					
	    if(intval($onearr[1]) !== intval($twoarr[1])){
	        return intval($onearr[1])>intval($twoarr[1])?$one:$two;
	    }
	    if(intval($onearr[2]) !== intval($twoarr[2])){
	        return intval($onearr[2])>intval($twoarr[2])?$one:$two;
	    }
	    return $one;
	}
	
			public function renew()
			{
				//获取传递的参数
				$postParams = $_POST;
				//我这里是使用的thinkPHP后台,在后台中可以直接设置ios版本和下载地址,这里获取就行
				 $ios=\think\Config::get("site.iosVersion");
				 $iosurl=\think\Config::get("site.iosUrl");
				
				 // 示例代码 update.php,此示例代码要求符合如下规则
				 // 1. manifest.json->基础配置->应用版本名称的值必须是以2个点号分隔开的3组数字,即  "数字.数字.数字" 比如 1.0.2   2.1.3 等,不可含有其他字符,且形式必须符合 数字.数字.数字 的形式
				 // 2. 该示例代码文件同目录下有 apk wgt等 文件夹,此文件夹下存放以版本号命名的apk包或wgt包,如果apk和wgt包版本号相同,则优先返回wgt,如 1.0.2.apk  1.2.4.apk 1.3.4.wgt等
				 // 3. 更新检测接口地址 http://a.com/update.php, apk下载url地址 http://a.com/apk/数字.数字.数字.apk或wgt
				
				 //0 不强制更新,1强制更新
				 $force=0;
				 //更新日志信息也是在后台直接设置,这里获取  
				 $log=\think\Config::get("site.reNewInfo");
				
				 // $new 存放最大的版本号,初始为 app中传来的版本号
				 $new=$_POST['version'];
				
				 // 存放apk或wgt的路径地址,根据自己服务器的路径规则设置,我这里使用了从根目录开始的路径
                 //由于我项目服务器上只有public目录下的文件运行直接访问,所以apk放在这里,如果你与我的不同,根据自己情况更改;$path=__DIR__.'/apk/';这是插件中的,它使用了先获取到当前文件目录,然后把apk目录与接口目录放在同一目录下
				 //  首先判断是否有待更新的资源 wgt
				 $path='/xxx/xxx/xxx/xxx.xxxx.xxx/public/apk/';
				 // 此处使用了exec调用linux上系统命令返回目录下所有的apk文件名称,需要php中启用exec函数
				 exec("find  {$path}  -type f -regex  '.*\(apk\|wgt\)'",$out);
				
				 foreach ($out as $k=>$v){
				     // 循环将最大的版本号赋给$new
				     $new = $this->getbig($new, substr(basename($v), 0, -4));

				 }
				 header('Content-Type:application/json;charset=utf-8');
				 //  如果版本号发生了变化,且 是热更新资源wgt,则无论ios还是android均返回资源wgt
				 if($new !=$_POST['version'] && file_exists($path.$new.'.wgt')){
				     //   如果是wgt,则无论ios还是android都返回
				     echo json_encode([
				         'code'=>0,
				         'msg'=>'ok',
				         'version'=>$new,
				         'url'=>'https://xxx.xxxx.xxx/apk/'.$new.'.wgt',
				         'log' => '新版本:v'.$new."\n".$log,
				         'force'=>$force
				     ]);
				     exit;
				 }
				
				 //  IOS 更新判断    
				 // $_POST 请求数据为 [version=>'数字.数字.数字 形式版本号',platform=>'android或ios']
				 if($_POST['platform']=='ios'){
				     // 如果是ios,则直接填写最新版本号和商店下载地址
				     $new=$ios;//最新的版本号
				     $url=$iosurl;//苹果商店地址
				     // getbig($new,$_POST['version']) 函数会返回 所传入版本号参数中较大的那个
					 $big = $this->getbig($new,$_POST['version']);
				     if($_POST['version'] != $big){
				         // 如果返回值不等于 $_POST['version'],说明 $new 是新版本
				         // 返回地址
				         echo json_encode([
				             'code'=>0,
				             'msg'=>'ok',
				             'version'=>$new,
				             'url'=>$url,
				             'log'=>$log,
				             'force'=>$force
				         ]);
				     }else{
				         echo json_encode([
				             'code'=>1,
				             'msg'=>'no',
										
				         ]);
				     }
				     exit;
				 }
				
				 //  android下判断  
				 // 如果 $new 无变化,则无更新
				 if($new==$_POST['version']){
				     echo json_encode([
				         'code'=>1,
				         'msg'=>'no',
						 'postParams'=>$postParams,
				     ]);
				      exit;
				 }else{
				     echo json_encode([
				             'code'=>0,
				             'msg'=>'ok',
				             'version'=>$new,
				             'url'=>'https://xxx.xxxx.xxx/apk/'.$new.'.apk',
				             'log'=>'新版本:v'.$new."\n".$log,
				             'force'=>$force
				     ]);
				 }
				
				 
			}
			
			
			
}
			

         注意:上面的代码有几个地方需要注意

                1、由于安卓和ios检测更新最后下载的方式不一样,在ios中,我们会跳转app store下载更新,所以代码中就会获取到配置文件的site.iosVersion和site.iosUrl,这两个我采用的方式是都在thinkPHP的后台的系统配置的中直接设置而无需每次更新还得改动服务器上的这个文件,如果你使用的不是这个,那也想ios可以跳转,可以每次更新的时候修改一下这个文件的$ios和$iosurl参数这里,写死,只是有些麻烦。(另外更新日志那我也采用后台填写的方式,如下图),这个ios商店下载地址就是你在商店更新版本之后,点击分享这个app,就可以拷贝链接了,经百度说似乎每次更新这个链接都会发生改变,所以苹果商店一更新这里也得重新拷贝链接

                2、如下图,服务器上我把存放新版本apk的目录放在了项目目录的public的apk文件夹下,因为我的项目不允许直接访问除了public下其他的文件夹,你也可以根据自己实际环境更改,反正最后把路径放在$path中使其下载时能够访问到即可;还有就是记得启用服务器上exec函数,不然找不到这个apk文件,启用方法自行百度(一般会写在配置文件里或者宝塔上单独设置)

 

 差不多到这里完成了,经测试可以使用,有问题可以共同交流探讨!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2063177.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【专题】2023-2024中国游戏企业研发竞争力报告合集PDF分享(附原数据表)

原文链接&#xff1a; https://tecdat.cn/?p37447 在当今的数字时代&#xff0c;游戏产业已然成为经济与文化领域中一股不可忽视的重要力量。2023 年&#xff0c;中国自研游戏市场更是呈现出一片繁荣且复杂的景象&#xff0c;实际销售收入达到了令人瞩目的 2563.8 亿元&#x…

计算机毕业设计选题推荐-民宿可视化分析-Python爬虫-随机森林算法

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

Catf1ag CTF Web(九)

前言 Catf1agCTF 是一个面向所有CTF&#xff08;Capture The Flag&#xff09;爱好者的综合训练平台&#xff0c;尤其适合新手学习和提升技能 。该平台由catf1ag团队打造&#xff0c;拥有超过200个原创题目&#xff0c;题目设计注重知识点的掌握&#xff0c;旨在帮助新手掌握C…

易趋产品升级 | EasyTrack11_V2.0功能更新合集

近日&#xff0c;易趋PPM&#xff08;EasyTrack PPM&#xff09;为了帮助企业全面提升数字化项目管理能力&#xff0c;完成了新一轮的产品升级&#xff0c;从【个人空间】、【项目组合管理】、【合同与外包管理】。除了以上三大功能模块之外&#xff0c;其他升级项暂略。 1.个人…

Ajax技术详解

Ajax简介 Ajax 即 "Asynchronous Javascript And XML"&#xff08;异步 JavaScript 和 XML&#xff09;&#xff0c;是指一种创建交互式、快速动态应用的网页开发技术&#xff0c;无需重新加载整个网页的情况下&#xff0c;能够更新页面局部数据的技术。 为什么要使…

c++习题25-判断字符串是否回文

目录 一&#xff0c;题目 二&#xff0c;思路 三&#xff0c;代码 一&#xff0c;题目 描述 输入一个字符串&#xff0c;输出该字符串是否回文。回文是指顺读和倒读都一样的字符串。 输入描述 输入为一行字符串&#xff08;字符串中没有空白字符&#xff0c;字符串长度不…

Linux文件属性和打包压缩详解

1、文件属性体系 1.1 文件系统概述 [rootyunwei /]# ls -lhi 总用量 72K3505 lrwxrwxrwx. 1 root root 7 3月 7 2019 bin -> usr/bin 262152 dr-xr-xr-x. 5 root root 4.0K 12月 19 16:00 boot 399635 drwxr-xr-x 2 root root 4.0K 11月 5 2019 data1026 drw…

【数据结构】二叉树基础知识

0. 前言 在前面几期博客&#xff0c;我们已经学习过了各种线性的数据结构&#xff0c;顺序表、链表、栈、队列&#xff0c; 本期博客我们一起来学习一种非线性的结构——树 1. 树的概念及结构 1.1 树的概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>…

学习嵌入式第二十九天

ipc进程间通信方式 PC&#xff0c;即进程间通信&#xff08;Inter-Process Communication&#xff09;&#xff0c;是操作系统中不同进程之间交换数据的一种机制。以下是一些常见的IPC方式&#xff1a; 管道&#xff1a;用于父子进程或兄弟进程之间的通信。消息队列&#xff…

火绒一键修复所有dll缺失?教你快速修复dll错误问题

你的电脑是否遇到过dll文件缺失的状态&#xff1f;那么应该如何将dll文件进行修复&#xff0c;不知道大家有没有听过火绒和电脑dll修复工具一键修复所有的dll缺失&#xff1f;今天我们就来了解一下如何使用火绒和电脑dll修复工具修复电脑错误dll文件丢失的问题。 dll是什么&…

海南云亿商务咨询有限公司靠谱不?

在这个短视频与直播浪潮席卷而来的时代&#xff0c;抖音电商以其独特的魅力迅速崛起&#xff0c;成为无数商家争相入驻的新战场。作为这一领域的佼佼者&#xff0c;海南云亿商务咨询有限公司凭借其专业的服务、前瞻性的视野和实战型的策略&#xff0c;正引领着一批又一批的商家…

【C语言进阶】数据如何安家?C语言内存中的存储艺术深度解析

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ ⏩收录专栏⏪&#xff1a;C语言 “ 登神长阶 ” &#x1f921;往期回顾&#x1f921;&#xff1a;C语言调试 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀C语言数据在内存中的存储 &#…

如何应对突发技术故障和危机:开发团队的应急策略

开发团队如何应对突发的技术故障和危机&#xff1f; 在数字化时代&#xff0c;软件服务的稳定性对于企业至关重要。然而&#xff0c;即使是大型平台&#xff0c;如网易云音乐&#xff0c;也可能遇到突发的技术故障。网页端出现502 Bad Gateway 报错&#xff0c;且App也无法正常…

云计算day30

1. 配置⼀主⼆从mysql57 1. mycat对mysql8不完全⽀持 2. mysql8主从问题不⼤ get_pub_key1 1. gtids事务复制 2. 删除/etc/my.cnf 3. 同步data⽂件需要先停⽤mysql服务&#xff0c;删除data⽬录中的 auto.cnf 4. gtid模式以及经典模式都需要锁表 flush tables with rea…

6 - Linux PXE高效批量网络装机

目录 一、PXE概述 1.简介 2.优点 3.前提条件 二、搭建PXE远程安装服务器 三、实现Kickstart无人值守安装 一、PXE概述 1.简介 PXE&#xff08;Preboot eXcution Environment&#xff09;预启动执行环境&#xff0c;是由Intel公司开发的网络引导技术&#xff0c;工作在Cl…

工业4G路由器

设备概述 路由器是基于4G 技术研发的无线路由网关设备&#xff0c;除了具备传统路由器 的 VPN 、防火墙、 NAT 、 PPPoE 、 DHCP 等功能之外&#xff0c;还能支持 4G 无线拨号&#xff0c;提供最高可达 150Mbps 的无线高速带宽。路由器支持四个以太网接口&#xff0c;可更好…

招募活动投稿展示 | 感受科技温度,从一个 LLM 应用开始

活动介绍 谷歌开发者招募活动是专为 Google 技术的爱好者及开发者们开展的活动&#xff0c;旨在鼓励大家通过多种形式 (文章/视频/coding 等) 创作与 Google 技术相关的讲解分享、实践案例或活动感受等内容&#xff0c;展示代码、框架、平台在真实世界中的生动表现&#xff0c;…

【Win开发环境搭建】Redis与可视化工具详细安装与配置过程

&#x1f3af;导读&#xff1a;本文档提供了Redis的简介、安装指南、配置教程及常见操作方法。包括了安装包的选择与配置环境变量的过程&#xff0c;详细说明了如何通过修改配置文件来设置密码和端口等内容。同时&#xff0c;文档还介绍了如何使用命令行工具连接Redis&#xff…

商品数据获取api接口:电商API接口助力内部平台商品定价!

对于很多电商内部平台来讲&#xff0c;品牌方在为内部平台的商品定价时&#xff0c;通常会获取主流电商平台的商品数据以供参考&#xff0c;具体来看&#xff0c;这主要涉及以下步骤&#xff1a; 选择合适的API接口服务商。电商API接口能够提供来自多个主流电商平台的商品数据…