Cordova App 热更新 超详细教程

前言:

Cordova热更新的一些要点:

1、在不用重新安装App的情况下,更新你的代码。可以越过应用商店的审核步骤。
2、涉及的插件依赖发生变化时,无法使用热更新,需要去应用商店下载最新版本安装。
3、热更新不能完全替代你的更新方案,需要结合现有更新方案实施。

正文:

目前Cordova平台我找到的热更新方案有两种

  1. 使用cordova-hot-code-push插件 GitHub仓库地址
  2. 使用 cordova-plugin-code-push GitHub仓库地址

第一个插件,已经官宣不再维护了。第二个插件是微软官方提供的。果断选择第二个。
关于第一个插件的使用方式可以参考这篇文章 传送门

先放上我本地的开发环境:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

环境准备

先全局安装 code-push-cli

npm install -g code-push-cli

后来在实际使用中 提示code-push-cli后续不再支持了,推荐统一使用 appcenter-cli。我去Github仓库看了cli的帮助文档,写的比较简单。需要对应着 code push cli的帮助文档使用。所以本文仍采用 code-push-cli
在这里插入图片描述

npm install -g appcenter-cli

使用code-push-cli 登录

执行 code-push login 命令会打开浏览器窗口 登录code-push服务端
在这里插入图片描述

我用的github帐号登录,登录成功会返回一个权限token
在这里插入图片描述

复制token粘帖到命令行中,回车登录成功(如果粘贴一直失败,可以尝试点鼠标右键,我一直遇到这种情况)
在这里插入图片描述

使用code-push-cli在服务端创建应用

使用命令code-push app add 创建应用

code-push app add test_ios ios cordova
code-push app add test_android android cordova

在这里插入图片描述
执行上述命令会默认为每个应用生成两种部署类型(“Production"和"Staging”),我们通过这两种类型分别代表生产环境和开发环境。要注意记下你生成的这些Key值,它用来连接客户端和服务端。

如果忘记了,也可以执行以下命令查看

code-push deployment list <ownerName>/<appName> --displayKeys

其实也可以在Web端查看管理,但是需要引入相关sdk 传送门

在项目中集成热更新

1、安装插件
ionic cordova plugin add cordova-plugin-code-push
npm install @ionic-native/code-push

注:官方文档中有提到必须安装白名单插件 cordova plugin list ,一般在cordova
添加platform时就默认安装过了这个插件,最好还是检查plugins文件夹确认一下。

2、配置

在config.xml文件中添加如下配置允许与CodePush服务器通信

// 其实在添加platform时,这句已经自动帮你加上了
<access origin="*" />

或者

<access origin="https://codepush.azurewebsites.net" />
<access origin="https://codepush.blob.core.windows.net" />
<access origin="https://codepushupdates.azureedge.net" />

然后在config.xml中加入如下配置,这里的value也就是上一步添加项目时生成的DeploymentKey

<platform name="android">
    <preference name="CodePushDeploymentKey" value="YOUR-ANDROID-DEPLOYMENT-KEY" />
</platform>
<platform name="ios">
    <preference name="CodePushDeploymentKey" value="YOUR-IOS-DEPLOYMENT-KEY" />
</platform>

也可以选择不配置,在代码中动态控制,如下:
在项目中新建一个config文件,然后在代码中根据环境传入不同的key。(CodePush插件的一些方法允许传入key,并帮你改写config文件中配置,比如sync 和 checkForUpdate),具体的可以参考后面正式代码

export const config = {
    /**
     * 是否是debug环境
     */
    isDebug: true,
    /**
     * 热更新部署时用于链接项目的key
     */
    codePushDeploymentKey: {
        android: {
            Production: '你的android Production key ',
            Staging: '你的android Staging key'
        },
        ios: {
            Production: '你的ios Production key',
            Staging: '你的ios Staging key'
        }
    }
};
3、加入热更新代码

代码可以加在任何你想触发热更新检查的地方,比如启动App时。
在此之前可以先捋一下思路,通常热更新的场景有两种:

  1. 静默更新,不弹框提示用户。更新完以后,等待下次App启动应用更新或者直接强制重启App应用更新(不推荐强制重启)。
  2. 弹框提示用户,有新的更新内容,用户可选忽略本次更新(下次进入App时重新提示),当用户选择确认更新时,显示loading下载进度条,并执行下载更新。更新完毕后可以弹框提示让用户选择是否立刻重启。若不重启则下次启动App时自动应用更新。

ios不允许热更新,所以我们不能弹框,一般走静默更新,而谷歌安卓规定需要弹框提示。

后台静默更新

import { Component } from '@angular/core';
import { Platform } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';
import { CodePush } from '@ionic-native/code-push/ngx';
import { config } from './app.config';
@Component({
    selector: 'app-root',
    templateUrl: 'app.component.html',
    styleUrls: ['app.component.scss']
})
export class AppComponent {
    constructor(
        private platform: Platform,
        private splashScreen: SplashScreen,
        private statusBar: StatusBar,
        private codePush: CodePush
    ) {
        this.initializeApp();
    }

    initializeApp() {
        this.platform.ready().then(() => {
            this.statusBar.styleDefault();
            this.splashScreen.hide();
            // 可以在这里比对数据库判断是否有大的版本变更,如果有就走更新整个安装包的逻辑,如果没有就走热更新
            if(this.checkAppVersion()) {
            	// do something
            } else {
            	this.loadCodePush();
            }
        });
    }
    // 执行热更新逻辑
    loadCodePush() {
        let deploymentKey = '';
        if (this.platform.is('ios')) {
            deploymentKey = config.isDebug ? config.codePushDeploymentKey.ios.Staging : config.codePushDeploymentKey.ios.Production;
        } else if (this.platform.is('android')) {
            deploymentKey = config.isDebug ? config.codePushDeploymentKey.android.Staging : config.codePushDeploymentKey.android.Production;
        }
       // 下载进度回调
       const downloadProgress = (progress) => {
	        console.log('下载进度:', progress);
	        console.log(`Downloaded ${progress.receivedBytes} of ${progress.totalBytes}`);
        };
        // 开始同步
        // 除非需要自定义UI和/或行为,否则建议大多数开发人员在将CodePush集成到他们的应用程序时使用这种方法。
        //(推荐悄悄的在后台安装然后等待下一次重启时应用更新)
        this.codePush.sync({deploymentKey}, downloadProgress).subscribe(syncStatus => {
            console.log('同步状态:', syncStatus);
        });
    }
}

弹框提示更新

import { Component } from '@angular/core';
import { Platform } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';
import { CodePush, InstallMode } from '@ionic-native/code-push/ngx';
import { config } from './app.config';
@Component({
    selector: 'app-root',
    templateUrl: 'app.component.html',
    styleUrls: ['app.component.scss']
})
export class AppComponent {
    constructor(
        private platform: Platform,
        private splashScreen: SplashScreen,
        private statusBar: StatusBar,
        private codePush: CodePush
    ) {
        this.initializeApp();
    }

    initializeApp() {
        this.platform.ready().then(() => {
            this.statusBar.styleDefault();
            this.splashScreen.hide();
            // 可以在这里比对数据库判断是否有大的版本变更,如果有就走更新整个安装包的逻辑,如果没有就走热更新
            if(this.checkAppVersion()) {
            	// do something
            } else {
            	this.loadCodePush();
            }
        });
    }
    // 加载热更新
    loadCodePush() {
        let deploymentKey = '';
        if (this.platform.is('ios')) {
            deploymentKey = config.isDebug ? config.codePushDeploymentKey.ios.Staging : config.codePushDeploymentKey.ios.Production;
        } else if (this.platform.is('android')) {
            deploymentKey = config.isDebug ? config.codePushDeploymentKey.android.Staging : config.codePushDeploymentKey.android.Production;
        }
        // 弹框配置
        const dialogOption = {
            optionalUpdateMessage: '是否马上更新',
            updateTitle: '发现新版本',
            optionalInstallButtonLabel: '确定',
            optionalIgnoreButtonLabel: '忽略',
        };
        // 获取下载进度的回调函数
        const downloadProgress = (progress) => {
            console.log('下载进度:', progress);
            console.log(`Downloaded ${progress.receivedBytes} of ${progress.totalBytes}`);
        };
        // 自动执行检查更新,有更新则在后台下载和安装它。如果本次更新设置了强制更新,会立马重启App
        this.codePush.sync({
            updateDialog: dialogOption,
            deploymentKey,
        }, downloadProgress).subscribe(syncStatus => {
            console.log('同步状态:', syncStatus);
        });
    }
}

向热更新服务器推送更新

进入项目的根目录下,使用 code-push release-cordova 发布应用

code-push release-cordova test_ios ios --description "ios code push"
code-push release-cordova test_android android  --description "android code push"

使用命令 code-push deployment list 查看发布状态

code-push deployment list test_android

其他常用命令

//给app在热更新服务器上创建应用
code-push app add <appName> <os> <platform> 

//删除应用
code-push app rm <appName>

//查看热更新服务器上有哪些应用
code-push app list

//发布应用
code-push release-cordova <appName> <platform> [options]
 Options参数:
  --deploymentName, -d ..指定部署的类型.默认"Staging",可以选择"Production"或其他  自定义类型
  --description, --des ..添加描述
  --mandatory, -m .......指定此版本是否为强制更新版本
  例1:发布更新
  code-push release-cordova ionic2_tabs_android android --des ""2:部署"Production"状态的更新,即生产环境的热更新部署使用这句命令
  code-push release-cordova ionic2_tabs_android android  -d "Production" --des ""
  注意:一般生产环境的app是压缩过的,所以在发布正式环境热更新之前,先执行"ionic build --prod"压缩代码
  例3:部署ios应用的更新
  code-push release-cordova ionic2_tabs_ios ios --des ""4:添加-m参数强制更新,code-push插件从服务端下载完代码,会立即自动重启app
  code-push release-cordova ionic2_tabs_android android  -m --des ""

//查看部署状态
code-push deployment list <appName>1:
  code-push deployment list ionic2_tabs_android
  例2:查看部署状态及key值,忘记key就这样找
  code-push deployment list ionic2_tabs_android -k

//清空部署记录
code-push deployment clear <appName> <deploymentName>:清空Staging状态的部署记录
code-push deployment clear ionic2_tabs_android Staging

//添加部署状态,默认只有"Staging"和"Production"两中状态
code-push deployment add <appName> [deploymentName]

//删除自定义的部署状态
code-push deployment rm <appName> <deploymentName>

Api解读
官方文档
跟官方文档的调用方式不一样是因为,在项目内引入了ionic-native/code-push ,它对官方的方法使用promise做了封装。但是方法名都分别对应着官方文档的方法名。

 // 检查是否有更新  返回null则代表未检测到更新
 const checkForUpdateRes = await this.codePush.checkForUpdate(deploymentKey);
 console.log('检查是否有更新:', checkForUpdateRes);
 
 // 获取当前更新包的相关信息 如 描述信息、安装时间、大小
 const getCurrentPackageRes = await this.codePush.getCurrentPackage();
 console.log('获取当前安装包的相关信息:', getCurrentPackageRes);
 
 // 为已下载并安装,但尚未通过重新启动应用的更新(如果存在)检索相关数据。
 const getPendingPackageRes = await this.codePush.getPendingPackage();
 console.log('获取已下载完成更新包的相关信息:', getPendingPackageRes);

 // 通知本次热更新安装成功。如果你手动检查并安装更新(即不使用sync方法来处理所有更新),
 // 那么这个方法必须被调用;否则,CodePush会将更新视为失败,并在应用程序下一次重启时回滚到之前的版本。
 this.codePush.notifyApplicationReady();
 
  // 重启App
 this.codePush.restartApplication();
 
 // 自动执行检查更新,有更新则在后台下载和安装它。如果本次更新设置了强制更新,会立马重启App
 // 除非需要自定义UI和或行为,否则建议大多数开发人员在将CodePush集成到他们的应用程序时使用这种方法。
 // 调用此方法时,插件内部会自动帮你执行上面几种方法
 this.codePush.sync(syncOptions?: SyncOptions, downloadProgress?: SuccessCallback<DownloadProgress>).subscribe(syncStatus => {
     console.log('同步状态:', syncStatus);
 });
 

关于sync 方法中相关参数说明:


 this.codePush.sync(syncOptions?: SyncOptions, downloadProgress?: SuccessCallback<DownloadProgress>)
 .subscribe(syncStatus => {
     console.log('同步状态:', syncStatus);
 });
 
SyncOptions:{
    /**
     * 用于指定用于安装操作的安装模式。默认为InstallMode.ON_NEXT_RESTART(即下次重启时应用)
     */
    installMode?: InstallMode;
    /**
     * 如果installMode === ON_NEXT_RESUME,则表示当应用程序恢复更新安装之前,应用程序在后台运行所需的最短时间(以秒为单位)。
     */
    minimumBackgroundDuration?: number;
    /**
     * 用于指定强制更新时用于安装操作的安装模式(即像热更新服务器推送更新包时的配置)。
     * 这是可选的,默认为InstallMode.IMMEDIATE。(即立刻重启)
     */
    mandatoryInstallMode?: InstallMode;
     /**
     * 如果设置该值,将忽略前一个被回滚的更新。默认值为true。
     */
	ignoreFailedUpdates?: boolean;
    /**
     * 用于在同步过程中启用、禁用或自定义用户交互。
     * 如果设置为true,用户将收到弹框提示是否确认更新(如果设置了强制推送,则不会显示取消按钮)
     * 如果传递 UpdateDialogOptions 则对弹框进行自定义操作
     */
    updateDialog?: boolean | UpdateDialogOptions;
    /**
     * 在检查更新时重写config.xml中设置的key。
     */
    deploymentKey?: string;
}
enum InstallMode {
    /**
     * 立即重启并应用更新
     */
    IMMEDIATE = 0,
    /**
     * 下一次重启时应用更新
     */
    ON_NEXT_RESTART = 1,
    /**
     * 当应用程序切入后台,然后重新进入时应用更新
     */
    ON_NEXT_RESUME = 2
}
enum SyncStatus {
    /**
     * 当前是最新的
     */
    UP_TO_DATE,

    /**
     * 更新是可用的,它已经下载、解压缩并复制到部署文件夹。
     * 在用SyncStatus调用的回调完成之后。UPDATE_INSTALLED,应用程序将用更新后的代码和资源重新加载。
     */
    UPDATE_INSTALLED,

    /**
     * 更新是可用的,但用户选择忽略此次更新(仅适用于使用updateDialog时)
     */
    UPDATE_IGNORED,

    /**
     * 同步操作期间发生错误。这可能是在与热更新服务器通信、下载或解压缩更新时出现的错误。
     * 控制台日志应该包含关于所发生事件的更多信息。本次操作中没有应用任何更新。
     */
    ERROR,

    /**
     * 另一个同步已经在运行,因此此同步尝试已中止。
     */
    IN_PROGRESS,

    /**
     * 中间状态-正在查询热更新服务器以进行更新。。
     */
    CHECKING_FOR_UPDATE,

    /**
     * 中间状态 - 更新可用,并向用户显示最终确认对话框。(这仅适用于updateDialog使用时)
     */
    AWAITING_USER_ACTION,

    /**
     * 中间状态 - 正在从热更新服务器下载一个可用的更新。
     */
    DOWNLOADING_PACKAGE,

    /**
     * 中间状态 -  下载了一个可用的更新并即将安装。
     */
    INSTALLING_UPDATE
}

其他问题

走热更新还是整体包更新

在前言部分热更新的注意点中也提到了,当项目依赖的插件发生变化时,比如新增了一个cordova插件,或者新import了插件中的一个类型,重新打包推送到热更新服务器后,热更新就无法正常运行了。推送直接就会报Conflict错误,解决方法往后看。
这时就必须提示用户有版本更新,然后去应用商店下载,或者采用后台下载的方式。

所以在代码中我们一般要根据版本号做出判断,是走热更新还是包更新。
但是我实际踩坑下来,当更改了插件内容,并推送了一个更新版本的包0.0.3上去,如下图(我手机装的是0.0.2版本),在调试了官方提供用于检测版本的Api checkForUpdate后,发现并不能检测到更新,直接返回null。
在这里插入图片描述
在这里插入图片描述
只是在插件代码执行过程中打印了一句 :更新是可用的,但它针对的是比您当前运行的更新的二进制版本。
就算是我想通过判断字符串这种蠢办法,我也拿不到这个log啊!猜测这个方法应该是通过version为条件进行查找的。
所以对于怎么判断有大的版本变更,还是要通过比对数据库的方式,每次发布新版本后,往数据库写入一条数据,然后app启动时请求该表,比对当前版本和数据库的版本大小,如果有新版本,则执行整体更新。针对App更新方案后续我再单独写一篇文章。

后续:今天去扒了扒codepush的源码,看到了这里:
在这里插入图片描述
这个 remotePackageOrUpdateNotification 对象里是有version版本的,所以可以魔改一下代码,或者给codepush的原型对象上加一个方法,把这个version传递出去,这样就可以省去比对数据库版本时,多的那一次请求了。

关于苹果 App Store禁止热更新的说明:

虽然苹果的开发者协议完全允许执行JavaScript和资产的空中更新(这是实现CodePush的原因),但应用显示更新提示是违反他们的政策的。
因此,建议ios应用商店发布的应用在调用sync时不启用updateDialog选项,而谷歌Play和内部发布的应用(如Enterprise, Fabric, HockeyApp)可以选择启用 或者自己定制弹框。

涉及到插件变化,推送后报错:

如果有插件发生变化,亲测哪怕是多 import 了插件内的某个类型(猜测是因为tree shaking,加入了新的import后,依赖包发生了变化),发布内容就会报如下错误。code push此时认为这是两个版本了,无法推送代码。
在这里插入图片描述
解决方案:修改版本号(新的版本号必须大于当前),并重新发布。
注意:虽然向热更新服务器成功推送了更新,但是由于版本号发生变化,热更新并不能检测到,需要提示用户去应用商店下载最新的App并重新安装,或者走后台下载,然后弹框提示的方式。参考 传送门
在这里插入图片描述
这里还有个问题,如果你在应用商店上传了新的app,同时向热更新服务器推送了新版本的包,在用户从应用商店装了新的App后,向热更新服务器请求检测是否有新版本,还是会检测到,哪怕你已经从商店装了最新的。
因为每次向热更新服务器推送新版本后,热更新服务器就会生成差异包,此时用新的版本号查询,就会查询到差异包存在,还是会执行一遍替换。
所以这里建议向热更新服务器推迟一个版本,等待下一次需要热更新时再往热更新服务器去推送。

插件自带的更新提示弹框实在太丑了:

不仅丑,还不支持带格式的文本显示,就是一个单纯的字符串!建议自己定制一套样式,然后先调用检测更新的那个api,如果有更新就弹出自定义的提示框!
在这里插入图片描述

本文部分内容参考自 传送门
如果觉得代码放在微软不安全或者觉得每次翻墙下载更新包太慢,可以自己搭建热更新服务器 参考 传送门

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第一步、将 myApp 目录下 config.xml <chcp> <auto-download enabled="true" /> <auto-install enabled="true" /> <config-file url="http://192.168.1.34:8080/myApp/www/chcp.json" /> <local-development enabled="true" /> </chcp> 地址改为自己的地址。 第二步、将myApp\platforms\android\app\src\main\res\xml目录下 config.xml <chcp> <auto-download enabled="true" /> <auto-install enabled="true" /> <config-file url="http://192.168.1.34:8080/myApp/www/chcp.json" /> <local-development enabled="true" /> </chcp> 第三步、myApp\platforms\android\app\src\main\assets 目录下 .chcpenv { "content_url": "http://192.168.1.34:8080/myApp/www/", "config_url": "http://192.168.1.34:8080/myApp/www/chcp.json" } cordova-hcp.json { "autogenerated": true, "update": "start", "content_url": "http://192.168.1.34:8080/myApp/www/" } 的 地址改为自己的 地址 第四步、将android项目导入Android Studio 第五步、 myApp\platforms\android\app\src\main\assets 目录下 执行 cordova-hcp build 命令后将www 目录下的所有文件拷贝到你自己的 文件服务器上。 第六步、启动服务器 第七步、发布安装app程序。 第八步、修改www目录下的文件信息,重新执行第五步。 第七步、再次打开app程序("update": "start",需两次)。 cordova-hcp.json { "autogenerated": true, "update": "start", "content_url": "http://192.168.1.34:8080/myApp/www/" } start - app启动时安装更新. 默认值. resume - app从后台切换过来的时候安装更新. now - web内容下载完毕即安装更新. 注意:网站的路径
Cordova App是一种跨平台的移动应用开发框架,可以基于HTML、CSS和JavaScript构建应用程序,同时支持Android和iOS等多个平台。在Cordova App中,可以通过使用插件来实现跳转功能。 跳转是指从一个页面或应用程序跳转到另一个页面或应用程序。在Cordova App中,可以通过使用插件来实现不同平台的跳转功能。例如,对于Android平台可以使用Cordova的InAppBrowser插件来打开一个浏览器窗口,并在该窗口中加载指定的URL页面。类似地,对于iOS平台可以使用Cordova的SafariViewController插件来打开一个在应用程序内部显示的浏览器窗口。 除了通过插件实现跳转功能,Cordova App还可以通过使用JavaScript代码来实现内部页面之间的跳转。通过使用Cordova提供的页面导航API,可以在不刷新整个应用程序的情况下,实现不同页面之间的跳转。可以通过在JavaScript中使用函数`window.location.href`或`window.location.replace`来修改页面的URL,进而实现页面跳转。 在Cordova App中进行跳转操作时,通常需要注意以下几点: 1. 合理使用跳转功能,避免过多跳转造成用户体验的不流畅。 2. 对于外部跳转,例如打开网页或其他应用程序,需要注意用户隐私和安全问题。 3. 在跳转过程中,可以通过传递参数来实现页面之间的数据传递,从而实现更复杂的业务逻辑。 总之,Cordova App提供了丰富的跳转功能,可以通过插件和JavaScript代码来实现页面之间的跳转。开发者可以根据具体需求选择合适的方法和技术来实现应用程序中的跳转功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值