基于uniapp开发的APP,怎么实现自动升级

最近使用uniapp框架开发了一款APP,怎么让APP监测到有新的版本,并且点击新的版本实现自动升级呢,话不多说,请看下文。

封装一个弹窗组件,当监测到最新的APP版本高于现在的版本时,弹窗提示新的版本信息,包括版本号、版本更新内容、是否强制更新控制等。

一、src/components/updateModal/index.vue,template代码如下:
<template>
  <view>
    <uni-popup
      class="popup-wrap"
      :is-mask-click="!versionObj.forceUpdate"
      ref="popup"
      type="center"
      background-color="#fff"
    >
      <view class="popup-content-wrap">
        <view class="title">New version detected</view>
        <view class="content">
          <view class="version-no">version: v {{ versionObj.version }}</view>
          <view>Update content:</view>
          <rich-text :nodes="versionObj.releaseNotes"></rich-text>
        </view>
        <view class="btn-wrap">
          <view @tap="close" v-if="!versionObj.forceUpdate" class="cancel">cancel</view>
          <view @tap="ok" class="confirm">confirm</view>
        </view>
      </view>
    </uni-popup>
    <!-- Android APP下载更新进度条弹窗 -->
    <uni-popup
      class="download-popup-wrap"
      :is-mask-click="false"
      ref="progressPop"
      type="center"
      background-color="#fff"
    >
      <view class="popup-content-wrap">
        <view class="title">Downloading, please wait</view>
        <view class="content">
          <zui-progress-bar :value="percent" />
        </view>
      </view>
    </uni-popup>
  </view>
</template>

二、在mounted生命周期中,判断是否需要更新APP:
async mounted() {
  // 获取最新版本入参
  const versionParams = {
    appId: config.systemAppId,
    platform: this.systemInfo?.platform, // 全局状态里的平台字段
  }
  // 查询接口获取最新版本信息
  const res = await versionsService.getLatestVersion(versionParams)
  if (res.code === 200 && res.data) {
    const version = res.data?.version
    // 封装的checkVersion方法,拿到最新的版本信息(包括版本号、更新内容、是否强制更新)与现有版本号对比,判断是否需要更新
    const needUpdate = checkVersion(version)
    this.versionObj = res.data
    // 需要更新弹窗提示
    if (needUpdate) {
      this.$refs.popup?.open()
    }
  }
},
三、弹窗提示,点击确认按钮开启更新
methods: {
  ok () {
    // 判断环境,区分不同的资源地址
    const { VUE_APP_ENV } = config
    const baseUrl =
      VUE_APP_ENV === 'production'
      ? 'https://api.****.net'
      : 'https://api-uat.***.net'
    const that = this
    
    // 获取手机是iOS系统还是Android系统
    // 这里this.systemInfo是项目中设置的一个全局的系统状态
    const platform = this.systemInfo?.platform
    if (platform === 'android') {
      // android端处理升级的逻辑
      // 打开进度条弹窗
      this.$refs.progressPop.open()
      // 关闭APP版本更新信息弹窗
      this.$refs.popup?.close()
      // 对应的apk、ipa、plist文件已上传至文件资源服务器,上传文件的时候需要对应把版本号、版本更新信息作为入参一并传给后端
      // 上传成功后会生成对应的版本id,前端拿到版本id然后拼接对应的文件路径、文件名称即可获得完整资源地址
      // 以上建议读者自行开发一个对应的包版本管理的页面

      // 使用uniapp的plus的api创建下载任务
      const dtask = plus.downloader.createDownload(
        `${baseUrl}/fileApi/***/${that.versionObj.versionId}/***.apk`,
        {
          filename: `_downloads/***{that.versionObj.version}.apk`,
        },
        function (d, status) {
          if (status === 200) {
            // 下载成功,使用plus api安装
            plus.nativeUI.showWaiting('开始安装...')
            plus.runtime.install(
              d.filename,
              {},
              function () {
                // 安装成功,数据初始化
                uni.removeStorageSync('userInfo')
                uni.removeStorageSync('state')
                plus.nativeUI.closeWaiting()
                that.$refs.progressPop.close()
              },
              function (e) {
                plus.nativeUI.closeWaiting()
                plus.nativeUI.alert(`安装失败[${e.code}]:${e.message}`)
                that.$refs.progressPop.close()
              }
            )
          } else {
            plus.nativeUI.alert(`下载更新文件失败:${status}`)
            that.$refs.progressPop.close()
          }
        }
      )

      // 监听下载任务的进度
      dtask.addEventListener(
        'statechanged',
        function (d) {
          that.percent = d.totalSize ? parseFloat(d.downloadedSize) / parseFloat(d.totalSize) : 0
          // w.setTitle("已下载:" + (cur / (1024 * 1024)).toFixed(2) + "M/" + (total / (1024 * 1024)).toFixed(2) + "M");
        },
        false
      )
      dtask.start()
    } else {
      // ios处理逻辑,简单的多
      // 拼接ios包资源地址,⚠️注意iOS系统中,是打开plist文件,plist文件内关联了最新的ipa文件,实现app更新
      const url = `${baseUrl}/fileApi/***/${that.versionObj.versionId}/***.plist`
      // 抵调用plus api打开itms-services协议的链接,即可下载更新
      plus.runtime.openURL(
        `itms-services://?action=download-manifest&url=${url}`,
        (openURLres) => {
          uni.removeStorageSync('userInfo')
          uni.removeStorageSync('state')
          console.log('openURLres:', openURLres)
        }
      )
    }
  }
}
四、这里补充一下plist文件内容,文件后缀是.plist
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <!-- array of downloads. -->
    <key>items</key>
    <array>
      <dict>
        <!-- an array of assets to download -->
        <key>assets</key>
        <array>
          <!-- software-package: the ipa to install. -->
          <dict>
            <!-- 必填项。 the asset kind. -->
            <key>kind</key>
            <string>software-package</string>
            <!-- 必填项。App (.ipa) 文件的完全限定 HTTPS URL,经验证,这里可以支持http -->
            <key>url</key>
          	<!-- 关联ipa文件 -->
            <string>https://api-uat.***.net/fileApi/***/151/ipa/***.ipa</string>
          </dict>
          <!-- 57 x 57 像素的 PNG 图像,在下载和安装过程中显示。指定图像的完全限定 URL。经验证非必填项,但是官方说是必填项,而且有的话下载安装过程体验好些,所以最好有。 -->
          <dict>
            <key>kind</key>
            <string>display-image</string>
            <!-- optional. indicates if icon needs shine effect applied. -->
            <key>needs-shine</key>
            <true/>
            <key>url</key>
            <string>https://api-uat.***.net/***/image.57x57.jpg</string>
          </dict>
          <!-- 512 x 512 像素的 PNG 图像,表示 App Store 中相应的 App。经验证非必填项,但是官方说是必填项,酌情处理就好。 -->
          <dict>
            <key>kind</key>
            <string>full-size-image</string>
            <key>needs-shine</key>
            <true/>
            <key>url</key>
            <string>https://api-uat.***.net/***/image.512x512.jpg</string>
          </dict>
        </array>
        <key>metadata</key>
        <dict>
          <!-- 必填项。App 的包标识符,与 Xcode 项目中指定的完全一样 -->
          <key>bundle-identifier</key>
          <string>{包标识符}</string>
          <!-- required. the download kind. -->
          <key>kind</key>
          <string>software</string>
          <!-- optional. displayed during download; typically company name -->
          <key>subtitle</key>
          <string>{应用子名称}</string>
          <!-- 必填项。下载和安装过程中显示的 App 的名称 -->
          <key>title</key>
          <string>{下载后显示的app名称}</string>
        </dict>
      </dict>
    </array>
  </dict>
</plist>

posted @ 2024-01-18 11:42  coderInside  阅读(1158)  评论(0)    收藏  举报