【uniapp】---- 在 uniapp 实现 app 的版本检查、下载最新版本、自动安装

【uniapp】---- 在 uniapp 实现 app 的版本检查、下载最新版本、自动安装

1. 前言

很久之前就准备自己开发一个账单 app 出来自家使用,今年的事情比较少,先使用 uniapp 进行开发,因为没有太多的体验要求,而且 uniapp 的开发也是我比较熟悉的,因此就选择了 uniapp。由于需要家人和你关联使用,因此在数据存储上边不使用自己购买服务器的话就是一个问题!当时第一个解决方案想到的就是【坚果云】,这里不是打广告,而是我之前使用【计次郎】的时候,他需要你绑定一个云空间,然后将数据推送过去,但是在对接的时候,放弃了!然后又想到了 gitee ,所以本文章采用的就是 gitee 获取最新的版本,然后和当前版本进行对比,看看是否需要更新。

2. 实现步骤

  1. 检查版本更新,看看是否需要进行更新操作;
  2. 更新提示弹框,下载并更新应用;
  3. 确认创建下载任务,提示下载;
  4. 使用 plus.runtime.install 进行安装;
  5. 使用 plus.runtime.restart 进行重启。

3. 检查版本

3.1 实现分析

  1. 获取当前版本信息:从 versionConfig 中获取应用程序的当前版本号;
  2. 扫描APK文件夹中的所有文件:调用 getFilesInFolder('release/apk') 异步获取APK文件夹中的所有文件,如果未找到任何文件,记录日志并结束函数执行;
  3. 筛选符合版本格式的APK文件:使用正则表达式 /^\d+.\d+.\d+.apk$/ 过滤出标准格式的版本文件(如 1.0.1.apk ),如果没有符合格式的文件,记录日志并结束函数执行;
  4. 提取并排序版本号:从每个APK文件名中提取出版本号(移除 .apk 后缀),使用自定义的排序算法,按照语义化版本号规则(主版本.次版本.修订号)进行排序;
  5. 确定最新版本:通过排序后取最后一个元素的方式获取最新版本号,根据最新版本号查找对应的文件对象;
  6. 比较版本并更新状态:调用 compareVersions 方法比较最新版本与当前版本,根据比较结果设置 hasUpdate 标志(是否有更新可用);
  7. 更新组件状态:将最新版本号、更新标志和最新文件信息保存到组件的响应式数据中。

3.2 实现代码

javascript 复制代码
// 检查版本更新
async checkForUpdates() {
  try {
    // 获取当前版本
    const currentVersion = versionConfig.currentVersion

    // 获取apk文件夹中的所有文件
    const files = await getFilesInFolder('release/apk')
    if (!files || files.length === 0) {
      console.log('未找到任何更新文件')
      return
    }

    // 过滤出版本号格式的apk文件(如1.0.1.apk)
    const versionFiles = files.filter(
      (file) => file.name && /^\d+\.\d+\.\d+\.apk$/.test(file.name)
    )

    if (versionFiles.length === 0) {
      console.log('未找到版本格式的更新文件')
      return
    }

    // 提取版本号并排序,找到最新版本
    const versionNumbers = versionFiles.map((file) => {
      // 从文件名中提取版本号(去掉.apk后缀)
      return file.name.replace('.apk', '')
    })

    // 按版本号排序,找到最大版本号
    const latestVersion = versionNumbers
      .sort((a, b) => {
        const aParts = a.split('.').map(Number)
        const bParts = b.split('.').map(Number)

        for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
          const aPart = aParts[i] || 0
          const bPart = bParts[i] || 0

          if (aPart > bPart) return 1
          if (aPart < bPart) return -1
        }

        return 0
      })
      .pop()

    // 查找最新版本 file.name 是 1.0.1.apk
    const latestFile = versionFiles.find((file) => file.name === `${latestVersion}.apk`)

    // 比较当前版本和最新版本
    const hasUpdate = this.compareVersions(latestVersion, currentVersion) > 0

    // 更新数据
    this.latestVersion = latestVersion
    this.hasUpdate = hasUpdate
    this.latestFile = latestFile
  } catch (error) {
    console.error('检查更新失败:', error)
  }
}

4. 获取所有版本号

javascript 复制代码
// 查询gitee一个文件夹下有哪些文件
export async function getFilesInFolder(folderPath) {
  try {
    const apiUrl = `${basePath(folderPath)}/${folderPath}?${baseParams(folderPath)}`;
    const response = await httpRequest({
      url: apiUrl,
      method: 'GET'
    });
    if (response.ok) {
      const files = await response.json();
      return files;
    } else {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
  } catch (error) {
    console.error('查询Gitee文件夹下文件失败:', error);
    return false;
  }
}

5. 对比版本号

5.1 实现分析

  1. 版本号解析与转换:接收两个版本号字符串参数 v1 和 v2,使用 split('.') 将版本号按点号分割成多个部分,使用 map(Number) 将每个部分转换为数字类型,便于数值比较,得到两个数字数组 v1Parts 和 v2Parts;
  2. 逐位比较版本号:确定最大迭代次数为两个版本号数组长度的较大值,使用 for 循环从左到右(主版本到次版本再到修订号)依次比较每个版本部分,对于长度不同的版本号,较短数组的缺少部分视为0(通过 || 0 处理);
  3. 返回比较结果:如果当前比较位 v1Part > v2Part ,返回 1 (表示v1版本更新),如果当前比较位 v1Part < v2Part ,返回 -1 (表示v2版本更新),如果所有对应位都相等,返回 0 (表示两个版本相同)。

5.2 实现代码

javascript 复制代码
compareVersions(v1, v2) {
  const v1Parts = v1.split('.').map(Number)
  const v2Parts = v2.split('.').map(Number)

  for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {
    const v1Part = v1Parts[i] || 0
    const v2Part = v2Parts[i] || 0

    if (v1Part > v2Part) return 1
    if (v1Part < v2Part) return -1
  }

  return 0
}

6. 更新提示

javascript 复制代码
downloadUpdate() {
  if (!this.latestFile) {
    uni.showToast({
      title: '更新文件信息不完整',
      icon: 'none'
    })
    return
  }

  // 显示确认对话框
  uni.showModal({
    title: '版本更新',
    content: `发现新版本 ${this.latestVersion},是否立即更新?`,
    showCancel: true,
    confirmText: '立即更新',
    cancelText: '稍后更新',
    success: (res) => {
      if (res.confirm) {
        // 用户点击立即更新
        this.startDownload(this.latestFile.download_url)
      }
    }
  })
}

7. 下载最新版本

7.1 实现分析

  1. 显示加载提示:调用 uni.showLoading() 显示加载中的提示框,设置 mask: true 防止用户在下载期间进行其他操作;
  2. 创建下载任务:调用 uni.downloadFile() 创建下载任务,传入以下参数:
  • url : 要下载的APK文件URL;
  • header : 请求头,包含了Gitee访问令牌用于授权;
  • 定义了成功和失败的回调函数。
  1. 处理下载成功情况:在 success 回调中,首先隐藏加载提示;重置下载进度为0;检查HTTP状态码,若为200表示下载成功:
  • 显示"下载完成"的成功提示;
  • 调用 installApk() 方法安装下载好的APK文件,传入临时文件路径;
  • 若状态码不为200,表示下载失败,显示相应错误提示。
  1. 处理下载失败情况:在 fail 回调中,隐藏加载提示;重置下载进度为0;打印错误日志到控制台;显示"下载失败,请重试"的错误提示;
  2. 监听下载进度:由于部分字段没有返回,所以将效果屏蔽了。

7.2 实现代码

javascript 复制代码
startDownload(apkUrl) {
  uni.showLoading({
    title: '加载中...',
    mask: true
  })
  // 创建下载任务
  const downloadTask = uni.downloadFile({
    url: apkUrl,
    header: {
      Authorization: 'Bearer ' + GITEE_CONFIG.accessToken
    },
    success: (downloadResult) => {
      uni.hideLoading()
      this.downloadProgress = 0 // 重置进度
      if (downloadResult.statusCode === 200) {
        uni.showToast({
          title: '下载完成',
          icon: 'success'
        })
        // 下载成功,安装APK
        this.installApk(downloadResult.tempFilePath)
      } else {
        uni.showToast({
          title: '下载失败',
          icon: 'none'
        })
      }
    },
    fail: (err) => {
      uni.hideLoading()
      this.downloadProgress = 0 // 重置进度
      console.error('下载失败', err)
      uni.showToast({
        title: '下载失败,请重试',
        icon: 'none'
      })
    }
  })

  // 监听下载进度
  downloadTask.onProgressUpdate((res) => {
    console.log('下载进度', res.progress)
    console.log('已经下载的数据长度' + res.totalBytesWritten)
    console.log('预期需要下载的数据总长度' + res.totalBytesExpectedToWrite)
    // 实现下载进度条弹窗效果
    // this.downloadProgress = res.progress
    // this.downloadedBytes = res.totalBytesWritten
    // this.totalBytes = res.totalBytesExpectedToWrite
  })
}

8. 安装APK

8.1 实现分析

  1. 环境检查 :首先检查 plus 对象是否存在,如果不存在(表示当前环境不支持安装功能),则显示提示信息"当前环境不支持安装"并返回 false ;
  2. 执行安装 :使用 await plus.runtime.install() API执行APK安装,传入三个参数:filePath :APK文件的路径;配置对象:设置 force: false ,表示不强制安装。
  3. 安装成功处理 :当安装成功时:显示提示信息"安装成功,即将重启";使用 setTimeout 在1.5秒后调用 plus.runtime.restart() 重启应用。

8.2 实现代码

javascript 复制代码
async installApk(filePath) {
  if (!plus) {
    uni.showToast({
      title: '当前环境不支持安装',
      icon: 'none'
    })
    return false
  }
  await plus.runtime.install(
    filePath,
    {
      force: false // 是否强制安装
    },
    () => {
      uni.showToast({
        title: '安装成功,即将重启',
        icon: 'none'
      })
      // 安装成功后重启应用
      setTimeout(() => {
        plus.runtime.restart()
      }, 1500)
    },
    (err) => {
      console.error('安装失败', err)
      uni.showToast({
        title: '安装失败,请重试',
        icon: 'none'
      })
    }
  )
}

9. 总结

  1. 版本检查:通过与服务器端存储的版本号进行对比,判断是否有新版本可用;
  2. 下载最新版本:若有新版本,提示用户是否立即更新;若用户同意,调用下载接口获取最新APK文件;
  3. 自动安装:下载完成后,自动调用安装方法,无需用户手动操作;
  4. 错误处理:在整个过程中,若遇到任何错误(如网络问题、权限问题等),均进行相应提示和处理,确保用户体验。
相关推荐
崔庆才丨静觅1 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60612 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了2 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅2 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅3 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅3 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment3 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅3 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊3 小时前
jwt介绍
前端
爱敲代码的小鱼3 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax