Cordova热更新与App更新

2,064 阅读7分钟

我的位置:ljn1998codeing.love

cordova热更新分为两部分,网页静态内容更新和cordova插件更新,网页静态内容是基于cordova运行的,例如第三方登陆QQ、微信需要集成QQ、微信的插件才能实现

集成更新热更新插件

// 添加插件
cordova plugin add cordova-hot-code-push-plugin
// 全局安装热更新工具
npm或cnpm install -g cordova-hot-code-push-cli
// 在cordova根目录上执行命令添加cordova-hcp.json文件,该文件是热更新文件chcp.json的模版
cordova-hcp init

Running init
Please provide: Enter project name (required):  cordovaVueDemo  // 项目名称
Please provide: Amazon S3 Bucket name (required for cordova-hcp deploy):  // 亚马逊S3储存桶名字,可以跳过
Please provide: Path in S3 bucket (optional for cordova-hcp deploy):  // S3储存桶路径,可以跳过
Please provide: Amazon S3 region (required for cordova-hcp deploy):  (us-east-1)  // 亚马逊S3地区,可以跳过
Please provide: IOS app identifier:  // IOS应用程序商店的应用程序ID。App升级是跳转应用商店进行升级
Please provide: Android app identifier:  // Android App在应用商城的地址,或者App的下载地址
Please provide: Update method (required):  (resume) start // 执行更新的方法 start(启动应用程序时)  resume(恢复应用程序时,比如从控制台切换到前景)  now(下载完更新立即安装)
Please provide: Enter full URL to directory where cordova-hcp build result will be uploaded:  http://xxxxxx.com/www  // www资源文件在服务器上的地址
Project initialized and cordova-hcp.json file created.
If you wish to exclude files from being published, specify them in .chcpignore
Before you can push updates you need to run "cordova-hcp login" in project directory

// 另外生成cordova-hcp.json文件后还需要在文件中添加"autogenerated": true
// 如果不添加更新将无效,操作完成后完整的cordova-hcp.json文件应该是这样
// 添加iosMinVersion、androidVersion以控制App版本,插件更新需要重装app才能生效
{
  "name": "cordovaVueDemo", // 可为空
  "autogenerated": true, // 如果不添加,热更新会不能使用
  "ios_identifier": "", // 应用在App store id(可为空)
  "android_identifier": "", // 应用在应用商城上的地址或者App的下载地址(可为空)
  "update": "start", // 在应用启动时安装
  "content_url": "http://xxxxxx/cordova/www", // www文件在服务器上的地址
  "iosMinVersion": "1.0.0", // ios版本,通过这个判断是否需要重新下载app,一般是更新了cordova插件才需要改变这个
  "androidVersion": "1.0.0" // android版本,通过这个判断是否需要重新下载app,一般是更新了cordova插件才需要改变这个
  "updateLog": "更新日志"
}

在WWW文件夹生成chcp.json和chcp.mainfest文件

  • chcp.json 包含cordova-hcp.json模版中的信息、最新发布信息、资源时间、native side版本号

  • chcp.mainfest 包含热更新静态文件信息(资源文件名和对应的hash值,更新的时候会通过对应的hash值来匹配对应的文件达到精确更新)

// 执行命令
cordova-hcp build
// 执行完命令会在模版当中添加"release":"当前时间"字段,我们判断更新时就是通过这个字段进行判断
"release": "2021.06.17-16.30.20", // 唯一web项目版本号,用与热更新web内容的更新。(必需) 

config.xml配置

// 在config.xml下方找个位置添加
<chcp>
    <config-file url="http://xxxxxx/cordova/www/chcp.json" /> // 配置文件 chcp.json 从服务器上加载的路径(必须的配置项)
    <auto-download enabled="false" /> // 是否自动下载更新,默认为true,因为我是通过方法手动更新的,所以设置为false
    <auto-install enabled="false" /> // 是否自动安装更新,默认为true,因为我是通过方法手动更新的,所以设置为false
    <native-interface version="1" /> // 当前 native side 的版本号
</chcp> 

热更新部署

在这一步直接将www文件夹里的文件替换服务器上的,需要注意的是www文件在服务地地址一定要与"content_url":"http://************/cordova/www"和config.xml中<config-file url="https://************/cordova/www/chcp.json" /> 填写的地址一致,这是后重启App,热更新就应该能正常执行,但是我们将"auto-download" 和 "auto-install"设置为false,所以更新不会自动执行,所以我们接下来就要编写更新逻辑

更新逻辑编辑

// 我使用的是vue项目,所以我的写法就按照vue的来
// 创建util.js文件
class Util {
  // 配置热更新服务地址
  chcpConfigure() {
    return new Promise((resolve, reject) => {
      chcp.configure(
        { "config-file": process.env.VUE_APP_CHCP_SERVER },
        (err) => {
          if (err) {
            Toast("热更新地址配置出错");
            reject();
          } else {
            resolve();
          }
        }
      );
    });
  }
  // 获取热更新版本号
  chcpGetVersion() {
    return new Promise((resolve, reject) => {
      chcp.getVersionInfo((err, data) => {
        if (err) {
          reject();
        } else {
          resolve(data);
        }
      });
    });
  }
  // 检查服务器是否存在更新
  chcpFetchUpdate() {
    return new Promise((resolve, reject) => {
      chcp.fetchUpdate((err, data) => {
        if (err) {
          reject(err);
        } else {
          resolve(data);
        }
      });
    });
  }
  // 安装已下载更新
  chcpInstallUpdate() {
    return new Promise((resolve, reject) => {
      chcp.installUpdate(err => {
        if (err) {
          reject(err)
        } else {
          resolve()
        }
      });
    })
  }
} 
export default new Util();

// 在App.vue文件中编写逻辑
import util from "@/common/util";

export default {
  methods: {
    getAppChcpVersion() {
      util.chcpGetVersion().then((versionData) => {
        this.checkedAppUpdate(versionData, 0);
      });
    },
    checkedAppUpdate(versionData, type) {
      // type 0 自动检测  1 手动更新
      const self = this;
      // getOnlineVersion 函数是直接用axios.get方法请求线上服务器www的chcp.json文件,获取到里面的数据,用来对比
      getOnlineVersion().then((res) => {
        let iosMinVersion = Number(res.iosMinVersion.replace(/\./g, "")),
          androidVersion = Number(res.androidVersion.replace(/\./g, "")),
          appVersion = Number(versionData.appVersion.replace(/\./g, ""));
        // 判断允许增量还是必须下载完整App
        if (
          (util.isAndroid() && appVersion < androidVersion) ||
          (util.isIos() && appVersion < iosMinVersion)
        ) {
          self.$dialog
            .alert({
              title: "标题",
              message: "请前往并下载完整App",
              theme: "round-button",
            })
            .then(() => {
              window.location.href = "app下载地址";
            });
        } else {
          if (versionData.currentWebVersion != res.release) {
            // 判断是否强制更新
            if (res.updateType == 1) {
              self.$toast({
                message: "系统强制更新!!!",
                onClose() {
                  self.checkedHasFetchUpdate();
                },
              });
            } else {
              self.$dialog
                .confirm({
                  title: "软件更新",
                  message: res.updateLog,
                })
                .then(() => {
                  self.checkedHasFetchUpdate();
                })
                .catch(() => {});
            }
          }
        }
      });
    },
    checkedHasFetchUpdate() {
      this.$toast.loading({
        message: "更新中...",
        duration: 0,
      });
      util
        .chcpFetchUpdate()
        .then((data) => {
          util
            .chcpInstallUpdate()
            .then(() => {
              this.$toast("成功安装更新");
            })
            .catch((err) => {
              console.log(err);
              this.$toast("更新安装出错");
            });
        })
        .catch((err) => {
          console.log(err);
          this.$toast("更新包获取失败");
        });
    },
  },
  created(){
    document.addEventListener(
      "deviceready",
      () => {
        this.getAppChcpVersion();
      },
      false
    );
  }
} 
// 如果没问题的话到了这一步热更新就能正常执行,打开App时就会执行方法,有更新的话就会弹出提示要求更新 

热更新方法

方法名称回调参数说明
chcp.isUpdateAvailableForInstallation(error, data)判断是否存在已下载完成的更新可用于安装
chcp.fetchUpdate(error, data)获取服务器版本状态,如存在更新,则下载更新文件
chcp.installUpdate(error)安装已下载的更新

热更新事件

事件名称说明
chcp_updateIsReadyToInstall新版本成功下载,准备更新
chcp_updateLoadFailed插件无法从服务器中获取更新数据
chcp_nothingToUpdate插件从成功服务器中获取更新数据配置,但不存在新版本
chcp_beforeInstall更新安装前
chcp_updateInstalled更新成功安装后
chcp_updateInstallFailed安装更新出错
chcp_nothingToInstall未安装任何更新;可能是未先下载更新内容
chcp_beforeAssetsInstalledOnExternalStorage插件准备下载更新内容到存储中
chcp_assetsInstalledOnExternalStorage插件成功下载更新内容到存储中
chcp_assetsInstallationError插件下载更新内容出错;可能是因为存储空间不足等原因

错误代码

错误代码错误名称说明
1NOTHING_TO_INSTALL发送了安装更新的请求,但未找到任何更新内容
2NOTHING_TO_UPDATE不存在更新内容
-1FAILED_TO_DOWNLOAD_APPLICATION_CONFIG无法从服务器中下载更新配置文件;可能是配置文件不存在或存在网络问题
-2APPLICATION_BUILD_VERSION_TOO_LOW客户端版本低于更新要求;需重新下载新版本的APK或IPA
-3FAILED_TO_DOWNLOAD_CONTENT_MANIFEST下载更新内容校验文件出错;可能是content_url配置错误导致
-4FAILED_TO_DOWNLOAD_UPDATE_FILES检查chcp.manifest,里面罗列的所有文件必须放在服务器对应位置,而且每个文件的校验值必须和chcp.manifest里面的一致
-5FAILED_TO_MOVE_LOADED_FILES_TO_INSTALLATION_FOLDER移动下载的更新文件到安装目录时出错,可能是因为设备空间不足
-6UPDATE_IS_INVALID下载的更新包无效;可能是因为下载的文件和chcp.manifest里面的校验值不一致导致的
-7FAILED_TO_COPY_FILES_FROM_PREVIOUS_RELEASE从旧版本中拷贝www目录到新版本目录时出错,可能是因为设备空间不足
-8FAILED_TO_COPY_NEW_CONTENT_FILES复制新文件到安装目录出错,可能是因为设备空间不足
-9LOCAL_VERSION_OF_APPLICATION_CONFIG_NOT_FOUND从存储空间读取配置文件时出错;可能是因为配置文件被手动删除了;插件将读取上次配置
-10LOCAL_VERSION_OF_MANIFEST_NOT_FOUND从存储空间读取校验文件时出错;可能是因为校验文件被手动删除了;插件将读取上次配置
-11LOADED_VERSION_OF_APPLICATION_CONFIG_NOT_FOUND读取新版本的配置文件出错;发生在安装更新过程中;可能原因是用户手动删除了该文件;该文件夹将在下次App运行时还原
-12LOADED_VERSION_OF_MANIFEST_NOT_FOUND读取新版本的校验文件出错;发生在安装更新过程中;可能原因是用户手动删除了该文件;该文件夹将在下次App运行时还原
-13FAILED_TO_INSTALL_ASSETS_ON_EXTERNAL_STORAGE从安装目录复制www文件夹到存储卡时出错;可能是因为存储空间不足;发生在应用第一次启动的时候;该错误将导致插件无法正常工作
-14CANT_INSTALL_WHILE_DOWNLOAD_IN_PROGRESS在下载的过程中调用了安装更新的命令;必须得等下载完成后才可以调用安装命令
-15CANT_DOWNLOAD_UPDATE_WHILE_INSTALLATION_IN_PROGRESS在安装的过程中调用了下载更新的命令;必须得等安装完成后才可以调用下载命令
-16INSTALLATION_ALREADY_IN_PROGRESS在安装过程中又再次调用了安装命令
-17DOWNLOAD_ALREADY_IN_PROGRESS在下载过程中又再次调用了下载命令
-18ASSETS_FOLDER_IN_NOT_YET_INSTALLED在应用第一次启动的时候,插件正在复制www文件到存储空间的时候,调用了chcp 热更新的方法