实现 App 自动检测更新(Vue3 + Ionic + Cordova + Capacitor)

2023-11-02

目录

一. 实现 App 自动检测更新的原理

1. 效果

2. 原理

二. 实现 App 自动检测更新的实践

1. 需要安装的插件

1.1 capacitor

1.2 cordova

2. 封装 app-update-main.ts(应用检查更新 - 入口)

2.1 定义应用信息的全局变量

2.2 获取服务器上最新的应用信息

2.3 获取当前设备信息

2.4 获取当前应用信息

2.5 处理版本号

2.6 根据全局信息、当前平台,判断是否更新及所需方法

2.7 添加 询问用户是否更新 的提示框

3. 封装 app-update-auxiliary.ts(应用检查更新 - 辅助)

3.1 根据文件名,判断文件媒体类型

3.2 获取 存放下载文件 的基本路径

3.3 添加下载 安装包(.apk) 的方法

3.4 计算文件下载进度

3.5 添加下载进度提示框

3.6 添加文件是否打开的提示框

3.7 添加打开 安装包(.apk)的方法

4. App 自动检查更新 方法执行的位置


一. 实现 App 自动检测更新的原理

1. 效果

自动检测更新的场景:

  • App 打开时,自动检测更新
  • 在设置中,点击版本号时,自动检测更新

2. 原理

  • 检查当前应用信息(获取当前版本号、设备平台信息等等)
  • 获取服务器应用信息(获取服务器最新版本号、下载地址等)
  • 判断是否更新及如何更新(判断用户是否更新、判断需要下载的包、是否立即安装等)

接口返回服务器上存储的应用信息示例(此处用 Rap2 模拟接口):

{
  "data": {
    // Android 最新应用下载地址
    "androidUrl": "https://www.xxx.com.cn/Android/others/tongfu/tongfu-prod.apk",
    // Android 包名
    "apkName": "baoan.apk",
    // ios 最新应用下载地址
    "iosUrl": "itms-services://?action=download-manifest&url=https://www.xxx.com.cn/iOS/others/tongfu/tongfu-prod.plist",
    // Android 最新应用版本号
    "verAndroid": "1.0.6",
    // ios 最新应用版本号
    "verIos": "1.0.6"
  }
}

二. 实现 App 自动检测更新的实践

1. 需要安装的插件

1.1 capacitor

// @capacitor/app
npm install @capacitor/app
npx cap sync

// @capacitor/device
npm install @capacitor/device
npx cap sync

1.2 cordova

// File
npm install cordova-plugin-file
npm install @ionic-native/file
ionic cap sync

// File Transfer
npm install cordova-plugin-file-transfer
npm install @ionic-native/file-transfer
ionic cap sync

// File Opener
npm install cordova-plugin-file-opener2
npm install @ionic-native/file-opener
ionic cap sync

2. 封装 app-update-main.ts(应用检查更新 - 入口)

此文件包含了 获取服务器 apk信息、获取当前 apk信息、判断是否执行 apk更新的相关方法

2.1 定义应用信息的全局变量

const 定义 全局变量,否则 ts 会报错(比如使用 let)

// 全局变量 - 应用信息(默认)- 需要使用 const定义,否则会报错
const appInfomation = {
  currentPlatform: '', // 当前平台
  currentVersion: '', // 当前app版本
  serverAndroidVersion: '', // 服务器最新app版本 - Android
  serverAndroidAppUpdateUrl: '', // 服务器最新app下载地址 - Android
  serverIosVersion: '', // 服务器最新app版本 - IOS
  serverIosAppUpdateUrl: '', // 服务器最新app下载地址 - IOS
  apkName: '', // 服务器android安装包的名字
};

2.2 获取服务器上最新的应用信息

此处省略了 获取服务器信息的 接口调用 过程,仅展示获取接口数据后,应该将 2.1 中的变量填充上什么信息

  // 获取服务器上的应用信息
  const serverAppInfo = await getServerAppInfo();
  console.log("获取服务器上的应用信息", serverAppInfo);

  // 服务器上的安卓应用版本
  appInfomation.serverAndroidVersion = serverAppInfo.data.verAndroid; 
  // 服务器上的安卓应用下载地址
  appInfomation.serverAndroidAppUpdateUrl = serverAppInfo.data.androidUrl; 
  // 服务器上的苹果应用版本
  appInfomation.serverIosVersion = serverAppInfo.data.verIos; 
  // 服务器上的苹果应用下载地址
  appInfomation.serverIosAppUpdateUrl = serverAppInfo.data.iosUrl; 
  // 服务器上的应用下载名
  appInfomation.apkName = serverAppInfo.data.apkName; 

2.3 获取当前设备信息

需要获取设备信息的原因:

  • Android / Ios 的下载路径、安装过程等,都不太相同,需要根据设备平台进行定制

此处采用 capacitor 中的 Device插件,它可以返回当前设备平台是 android 还是 ios

import { Device } from '@capacitor/device';

/**
 * 获取当前的设备所属平台
 */
const getDeviceInfo = async (): Promise<string> => {
  const info = await Device.getInfo();
  return info.platform;
};

  // 填充全局变量 - 应用信息
  appInfomation.currentPlatform = nowPlatform; // 当前设备平台

2.4 获取当前应用信息

需要获取当前应用信息的原因:

  • 是为了获取版本号,与服务器上最新的版本号进行对比,看看是否需要更新

此处使用 capacitor 中的 App插件,它可以返回当前应用的版本号

/**
 * 获取用户当前正在使用的app版本
 */
async function getCurrentAppInfo(): Promise<any> {
  // 调用获取应用信息的API
  const appInfo: AppInfo = await App.getInfo();
  return appInfo;
}

// 填充全局变量 - 应用信息
appInfomation.currentVersion = currentAppInfo.version; // 当前应用版本号

2.5 处理版本号

需要 处理版本号 的原因:

  • 为了方便比较 当前应用版本号 和 服务器最新版本号

处理版本号的方法:

  • 将版本号中的 . (1.0.0)替换成 0,这样就能直接比较 1.0.0 和 1.0.6 了
/**
 * 处理版本号(1.0.0处理为10000)
 * @param num 版本号
 */
function handleVersion(num: string): number {
    console.log('处理后的版本号', num.split('.').join('0'));
    return Number(num.split('.').join('0'));
}

2.6 根据全局信息、当前平台,判断是否更新及所需方法

  • 如果是安卓平台更新,则需要传递服务器上的 apk包名,而 ios 不需要
  • 是否更新,就是对比处理过的版本号,如果不一致,就更新
  if (appInfomation) {
    if (appInfomation.currentPlatform === 'android') {
      updateApp(appInfomation.currentVersion, appInfomation.serverAndroidVersion, appInfomation.serverAndroidAppUpdateUrl, appInfomation.currentPlatform, appInfomation.apkName);
    } else if (appInfomation.currentPlatform === 'ios') {
      updateApp(appInfomation.currentVersion, appInfomation.serverIosVersion, appInfomation.serverIosAppUpdateUrl, appInfomation.currentPlatform);
    } else {
      console.log('获取版本信息失败!');
    }
  }

/**
 * 判断是否进行更新,判断使用哪种平台提示
 * @param currentVersion 当前应用版本
 * @param serverVersion 服务器上的应用版本
 * @param serverAppUpdateUrl 服务器上的应用下载地址
 * @param currentPlatform 当前设备平台
 * @param apkName 服务器上 android 安装包的名字【可选】
 */
export function updateApp(currentVersion: string, serverVersion: string, serverAppUpdateUrl: string, currentPlatform: string, apkName?: string) {
  // 是否更新
  let ifUpdate = false;
  if (currentVersion && serverVersion) {
    ifUpdate = handleVersion(currentVersion) < handleVersion(serverVersion);
  }
  // 如果需要进行更新
  if (ifUpdate) {
    // this.platform.ready().then(() => {
    if (currentPlatform === 'android') {
      // 触发 Anroid 平台的提示
      presentAlert(currentVersion, serverVersion, serverAppUpdateUrl, currentPlatform, apkName);
    } else {
      // 触发 Ios 平台的提示
      presentAlert(currentVersion, serverVersion, serverAppUpdateUrl, currentPlatform);
    }
  }
}

2.7 添加 询问用户是否更新 的提示框

提示框使用 Ionic 提供的 alertController 

提示框处理逻辑如下:

  • 下载app 及 自动打开app 的方法,需要根据 平台 及是否包含 apk包名 进行判断传参
  • 如果是更新安卓应用,是必须要使用 apkName 的哦
import { alertController } from '@ionic/vue';
import { apkDownloadAndOpen } from '@/utils/apk-file-transfer'

/**
 * 更新提示、判断执行下载安装包的方法
 * @param currentVersion 当前应用版本
 * @param serverVersion 服务器上的应用版本
 * @param serverAppUpdateUrl 服务器上的应用下载地址
 * @param currentPlatform 当前设备平台
 * @param apkName 服务器上 android 安装包的名字【可选】
 */
async function presentAlert(currentVersion: string, serverVersion: string, serverAppUpdateUrl: string, currentPlatform: string, apkName?: string) {
  const alert = await alertController.create({
    header: '发现新版本, 是否更新?',
    cssClass: 'update-pop',
    message: '<div class="versions">当前版本号:' + currentVersion + '</div><div class="versions">待更新版本号:' + serverVersion + '</div>',
    buttons: [
      {
        text: '取消',
        handler: () => {
          console.log('用户暂不更新应用');
        }
      },
      {
        text: '立即更新',
        handler: () => {
          if (currentPlatform === 'android' && apkName) {
            // 下载app
            console.log('准备执行 android 下载')
            // window.open('serverAppUpdateUrl')
            apkDownloadAndOpen(serverAppUpdateUrl, apkName, currentPlatform, false, false);
            // this.fileTransferService.downloadFile(serverAppUpdateUrl, apkName, false, true);
          } else {
            console.log('准备执行 ios/web 下载')
            // // this.browser.create(serverAppUpdateUrl);
            // this.browser.create(serverAppUpdateUrl, '_system', { usewkwebview: 'no' });
          }
        }
      }
    ],
  });
  alert.present();
}

3. 封装 app-update-auxiliary.ts(应用检查更新 - 辅助)

此文件包含了 apk下载、计算下载进度、下载完成后打开apk文件的相关方法

3.1 根据文件名,判断文件媒体类型

所谓文件名,就是之前传的 apkName(形如 baoan.apk,要根据这个后缀,判断文件媒体类型)

/**
 * 根据文件名称获取文件媒体类型
 * @param fileName 文件名称
 */
export function getFileMimeType(fileName: string) {
  // 文件名后缀
  const fileSuffix = getFileSuffix(fileName);
  // 文件类型
  let mimeType = '';
  switch (fileSuffix) {
    case 'txt':
      mimeType = 'text/plain';
      break;
    case 'docx':
      mimeType = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
      break;
    case 'doc':
      mimeType = 'application/msword';
      break;
    case 'pptx':
      mimeType = 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
      break;
    case 'ppt':
      mimeType = 'application/vnd.ms-powerpoint';
      break;
    case 'xlsx':
      mimeType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
      break;
    case 'xls':
      mimeType = 'application/vnd.ms-excel';
      break;
    case 'zip':
      mimeType = 'application/x-zip-compressed';
      break;
    case 'rar':
      mimeType = 'application/octet-stream';
      break;
    case 'pdf':
      mimeType = 'application/pdf';
      break;
    case 'jpg':
      mimeType = 'image/jpeg';
      break;
    case 'png':
      mimeType = 'image/png';
      break;
    case 'apk':
      mimeType = 'application/vnd.android.package-archive';
      break;
    default:
      mimeType = 'application/' + fileSuffix;
      break;
  }
  return mimeType;
}

3.2 获取 存放下载文件 的基本路径

Android 和 Ios 存放文件的基本路径是不一样的

  • 以 Android 为例,像这样:file:///storage/emulated/0/Android/data/io.ionic.starter/files/tongfu.apk
  • 注意:路径是带 .apk 的

获取基本路径需要通过 cordova 插件 —— File

  • 注意:TypeScript 语法中,获取 cordova 提供的全局变量,需这样获取:(window as any).cordova.file
/**
 * 获取存放文件的基础路径 android/ios
 * @param currentPlatform 当前平台
 */
export function getFileBasePath(currentPlatform: string) {
  let baseUrl = ''
  if (currentPlatform === 'android') {
    baseUrl = (window as any).cordova.file.externalDataDirectory;
    // baseUrl = 'files';
  } else if (currentPlatform === 'ios') {
    baseUrl = (window as any).cordova.file.dataDirectory;
    // baseUrl = 'NoCloud/';
  } else {
    return '';
  }
  return baseUrl;
}

  // 获取存放文件的基础路径 - 最后拼出来的路径是带 .apk 的
  const filePath = getFileBasePath(currentPlatform) + fileName;

3.3 添加下载 安装包(.apk) 的方法

下载安装包(.apk)需要通过 cordova 插件 —— FileTransfer

  • 传入的下载路径,需要特殊编码 const url = encodeURI(serverUrl);
  • 实例化 FileTransfer 时,得这么写:const fileTransfer = new (window as any).FileTransfer();

下载时,报错代码(Code3):

  • 理论上来讲,这是 连接失败 的报错
  • 在 Android Studio 中的 插件源码部分 打断点调试,发现在 判定白名单 这块逻辑中报错了,下载路径不在应用白名单里,无法下载
  • 因此,我把 FileTransfer 插件源码中,判断白名单的逻辑块 给注释了(...)

/**
 * 下载文件
 * @param fileTransfer 当前文件传输对象
 * @param url 下载地址
 * @param filePath 文件下载后的本地存放路径
 * @param mimeType 文件媒体类型
 */
export function fileDownload(fileTransfer: any, url: any, filePath: string, mimeType: string): any {
  fileTransfer.download(url, filePath, function(entry: any) {
    console.log("下载完成,文件位于: " + entry.toURL());
    // 关闭下载进度提示框
    closeProgressPop();
    // 打开询问用户是否打开刚下载的应用的提示框
    openFilePop(filePath, mimeType, '应用下载完成,是否立即更新?');
  }, function (err: any) {
    console.log('下载失败,报错信息:', err)
  }, true)
}

3.4 计算文件下载进度

计算文件下载进度 需要通过 cordova 插件 —— FileTransfer 中的 onprogress 属性

踩坑记录:

  • 最好按照官网的写法来,比如 onprogress 直接 = function() {},千万别写 () => {} 这种回调函数,可能会因此报错,最好参数也别改【0.0】

计算文件下载进度方法,添加在:

  • FileTransfer.download() 执行之前,new FileTransfer() 初始化之后
/**
 * 计算文件下载进度
 */
export function calProgress(fileTransfer: any) {
  // 下载进度
  let progressValue = 0;
  // 计算下载进度,不可以用回调,会失去效果
  fileTransfer.onprogress = function(progressEvent: any) {
    if (progressEvent.lengthComputable) {
        progressValue = progressEvent.loaded / progressEvent.total;
    } else {
        progressValue++;
    }
    // 将 0.01321... 这种下载进度转换为整数
    const showNumber = Math.floor(progressValue * 100);
    // 填充已下载文字提示信息
    document.getElementsByClassName('downed')[0].innerHTML = `已完成:${showNumber}%`;
    // 填充进度条外框样式 - style会失效,因此在此处修改
    document.getElementsByClassName('progress')[0]['style'].width = '100%';
    document.getElementsByClassName('progress')[0]['style'].height = '4px';
    document.getElementsByClassName('progress')[0]['style'].background = '#eee';
    // 填充进度条进度样式 - style会失效,因此在此处修改
    document.getElementsByClassName('blue')[0]['style'].display = 'block';
    document.getElementsByClassName('blue')[0]['style'].width = `${showNumber}%`;
    document.getElementsByClassName('blue')[0]['style'].height = '100%';
    document.getElementsByClassName('blue')[0]['style'].background = '#1890ff';
  };
}

3.5 添加下载进度提示框

此进度框最好写在 全局变量 里,因为用他的地方比较杂,如下:

  • 在 执行下载 / 计算进度 之前,需要创造一个下载进度提示框,也就是执行 createProgressPop();
  • 在下载完成(成功)之后,需要关闭这个提示框,也就是执行 closeProgressPop();
// 下载进度框
let alertPop: any;

/**
 * 初始化下载进度框
 */
export async function createProgressPop() {
  alertPop = await alertController.create({
    message: '<p class="title">正在下载更新,请稍等...</p><div class="progress"><span class="blue"></span></div><p class="downed">已完成:0%</p>',
    backdropDismiss: false
  });
  await alertPop.present();
}

/**
 * 关闭下载进度框
 */
export function closeProgressPop() {
  if (alertPop) {
    alertPop.dismiss();
    alertPop = null;
  }
}

3.6 添加文件是否打开的提示框

显而易见,这个方法在下载成功之后执行

/**
 * 询问是否打开文件的提示框
 * @param filePath 文件本地路径
 * @param fileMimeType 文件媒体类型
 * @param msg 询问文本
 */
export async function openFilePop(filePath: string, fileMimeType: string, msg = '是否直接打开文件?') {
  const alert = await alertController.create({
    message: msg,
    buttons: [
      {
        text: '取消',
        role: 'cancel',
        handler: () => {
          console.log('用户暂不打开文件');
        }
      }, {
        text: '打开',
        handler: () => {
          // 打开文件
          openFile(filePath, fileMimeType);
        }
      }
    ]
  });
  await alert.present();
}

3.7 添加打开 安装包(.apk)的方法

打开文件 需要通过 cordova 插件 —— FileOpen2

  • 此插件的 打开文件方法,需要传入的参数是:需要打开的文件路径、需要打开的文件媒体类型
/**
 * 打开文件
 * @param filePath 文件本地路径
 * @param fileMimeType 文件媒体类型
 */
 export function openFile(filePath: string, fileMimeType: string) {
  (window as any).cordova.plugins.fileOpener2.open(filePath, fileMimeType).then(
    () => {
      console.log('文件打开成功!');
    }).catch(() => {
      alert('文件打开失败,请重试!');
    }
  );
}

4. App 自动检查更新 方法执行的位置

Android 系统:

  • 在 app.vue 中引用并调用,可以在 setup() 里直接调用,也可以在 onMounted() 中调用

Ios 系统:

  • 应该在 浏览器 中,下载并打开
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

实现 App 自动检测更新(Vue3 + Ionic + Cordova + Capacitor) 的相关文章

随机推荐

  • verilog实现串口通信发送到数码管

    verilog实现串口通信 实现PC与FPGA的串口通信 按键数码管显示 时钟频率50MHZ 波特率115200 可以修改调整 串口输入数据 发送到数码管上进行显示 在vivado建立工程并综合 顶层模块代码如下 timescale 1ns
  • nginx配置vue项目添加访问前缀

    文章目录 前言 实现需求 Nginx配置访问前端 正确配置 注意点 alias的含义 举个栗子 静态文件及js等404错误 前言 最近 在搞一个SASS系统 将原有的单服务 每次卖出一套啥软件就需要部署一套环境 使得运维人员有些捉襟见肘 产
  • Android开源项目:捕鱼达人游戏源代码

    Android开源项目 捕鱼达人游戏源代码 这是一个Android上的开源项目 捕鱼达人游戏源代码 github上的地址链接是 https github com zhangphil Android BuYuDaRenGame git 内容和
  • 详解YAML语法及占位符语法

    一 设计一个YAML数据结构 首先我们提出这样一个需求 1 一个家庭有爸爸 妈妈 孩子 2 这个家庭有一个名字 family name 叫做 happy family 3 爸爸有名字 name 和年龄 age 两个属性 4 妈妈有两个别名
  • Windows中的时间(SYSTEMTIME和FILETIME)

    上一篇中介绍了C运行库中的时间处理函数 这一篇介绍Windows SDk中提供的时间函数 两种时间系统之间没有本质区别 事实上CRT时间是用Windows时间实现的 当然这是说的VC实现 同样提供本地时间和UTC时间之间的转换 不过CRT中
  • TP5 FIND_IN_SET和数组组合成查询条件

    直接上代码 我的是TP5 0 22版本 map exp Db raw FIND IN SET 字段所包含的值 字段 map status 1
  • vscode使用editorconfig插件以及.editorconfig配置文件说明(统一代码风格工具——editorConfig)

    文档 EditorConfig for VS Code EditorConfig官网 微软说明 editorconfig vscode github 简介 EditorConfig和Prettier一样 都是用来配置格式化你的代码的 这个格
  • Linux环境下安装的Oracle,客户端连接数据库缓慢

    在linux环境下安装的Oracle 客户端每次连接数据库时 都需要进行DNS查询 根据IP查询主机名 但由于DNS服务器不可达 故存在等待超时 导致连接数据库过慢 解决方案 注释掉server上 etc resolv conf中所有行
  • ftp下载文件失败get: Access failed: 550 Failed to open file. (t1.log)

    get Access failed 550 Failed to open file t1 log 原因是被SELinux安全访问控制策略限制了 科普 SELinux Security Enhanced Linux 是美国国家安全局 NAS
  • 虚拟化技术之 VMware Workstation教程(一)

    目录 第一章 虚拟化技术 1 1虚拟化技术简介 1 2主流的虚拟化厂商及产品 第二章 虚拟机的安装 2 1安装VMwareWorkstation 12 2 2物理机所需硬件 2 3在虚拟机中安装Windows 7操作系统 第三章 虚拟机的网
  • 什么是严格模式和混杂模式?如何区分?

    一 什么是严格模式和混杂模式 严格模式 标准模式 浏览器按照W3C标准解析代码 混杂模式 怪异模式 兼容模式 浏览器按照自己的方式解析代码 DOCTYPE的作用 lt DOCTYPE gt 声明叫做文件类型定义 DTD 声明的作用为了告诉浏
  • 【Go】锁相关

    文章目录 Mutex锁 mutex源码分析 Lock UnLock mutex两种运行模式 mutex normal 正常模式 自旋 mutex starvation 饥饿模式 锁的底层实现类型 RWMutex RWMutex 实现 其他共
  • Chat GPT是什么,初学者怎么使用Chat GPT,需要注意些什么

    目录 Chat GPT是什么 初学者怎么使用Chat GPT 使用Chat GPT需要注意什么 一些简单的prompt示例 Chat GPT是什么 Chat GPT是由OpenAI开发的一种大型语言模型 它基于GPT Generative
  • 超好用的图床-生成图片在线链接

    超好用的图床 最近一直在写博客 但是经常会遇到上传图片的问题 得生成图片的在线链接 也试过各种图床 推荐两个我自己觉得用起来比较舒服方便的图床网站吧 文章目录 超好用的图床 1 路过图床 2 大名鼎鼎的 SM MS 后话 1 路过图床 这个
  • 这次总结的有点多(python)

    关键字不定长参数 可以接收多个未定义参数的关键字赋值 关键字不定长参数的格式 def 函数名 kwargs 函数体 TypeError a is an invalid keyword argument for print def func
  • java中代码和注释缩进_java编码规范_缩进和注释

    1 缩进排版 Indentation 4个空格常被作为缩进排版的一个单位 缩进的确切解释并未详细指定 空格vs 制表符 一个制表符等于n个空格 视具体的编辑器而定 Eclipse默认一个制表符为4个字符 3 1行长度 Line Length
  • ldconfig用法

    1 ldconfig简介 参考 http man linuxde net ldconfiglinux中ldconfig的使用介绍 chenzixun0的博客 CSDN博客 主要是在默认搜寻目录 lib和 usr lib以及动态库配置文件 e
  • (Redis):string介绍及应用

    目录 数据存储类型介绍 string redis 数据存储格式 string 类型 string 类型数据的基本操作 单数据操作与多数据操作的选择 string 类型数据的扩展操作 string 作为数值操作 string 类型数据操作的注
  • 蓝桥杯-2017-魔方状态-python3

    标题 魔方状态 二阶魔方就是只有2层的魔方 只由8个小块组成 如图p1 png所示 小明很淘气 他只喜欢3种颜色 所有把家里的二阶魔方重新涂了颜色 如下 前面 橙色 右面 绿色 上面 黄色 左面 绿色 下面 橙色 后面 黄色 请你计算一下
  • 实现 App 自动检测更新(Vue3 + Ionic + Cordova + Capacitor)

    目录 一 实现 App 自动检测更新的原理 1 效果 2 原理 二 实现 App 自动检测更新的实践 1 需要安装的插件 1 1 capacitor 1 2 cordova 2 封装 app update main ts 应用检查更新 入口