• [Phonegap+Sencha Touch] 移动开发77 Cordova Hot Code Push插件实现自己主动更新App的Web内容


    原文地址:http://blog.csdn.net/lovelyelfpop/article/details/50848524



    插件地址:https://github.com/nordnet/cordova-hot-code-push


    以下是我对GitHub项目readme的翻译

    ——————————————————————————————————————————————


    Cordova Hot Code Push Plugin

    此插件提供了能够使cordova app自己主动更新web内容的功能。

    ​基本上, 你App中全部位于 www 文件夹内的文件都能够被自己主动更新.

    当你又一次公布新的app时-又一次打包了web内容: html 文件, JavaScript 代码, 图片等等. 一般有两种方式进行升级:

    1. 在appstore中上架新的app. 可是耗时比較长.

    2. 牺牲全部原生功能。每次打开都从远端站点载入. 可是假设没有网络,app就没法使用.

    此插件就为了解决问题而生. 当用户初次打开app - 它会将全部web内容复制一份到外部存储. 此后从外部存储载入web内容,而并不载入打包在app内部的web内容. app每次启动都会连接server检查更新并下载新的web内容. 假设下载了更新 - 此次更新内容将会在下次app启动时生效.

    这样, 你的app就得到了实时更新, 而且也能在离线的时候使用. 还有,插件同意你对web内容设置最小支持的app外壳版本号, 以保证新的web内容能够在旧的app外壳上执行.

    App Store能够上架这样的app吗? 能够... 仅仅要你更新后的web内容符合app一開始的功能. 假设本来是个计算器, 更新后变成了一个音乐播放器 - 这是会被禁止的.

    支持平台

    • Android 4.0.0 或以上.

    • iOS 7.0 或以上.

    文档

    安装

    须要cordova 5.0+

    cordova plugin add cordova-hot-code-push-plugin

    也可直接从 仓库url 安装(不稳定)

    cordova plugin add https://github.com/nordnet/cordova-hot-code-push.git

    插件安装完后,会推荐你安装Cordova Hot Code Push 命令行client. 此客户的能够帮助你:

    • 方便生成必须的app配置文件;

    • 启动本地server,监听开发模式下的web内容变更,并直接部署新版本号.

    当然,你也能够不用这个命令行client, 仅仅是用了它会更加方便.

    从低版本号迁移

    从 v1.0.x 到 v1.1.x

    在版本号 1.0.x 的时候,本地开发模式集成到了此插件里面. 从 v1.1.x 開始这部分功能作为了此插件的一个扩展,移到了这里. 由于 v1.0 版本号为了支持ios的Swift做了一些优化 - 升级到 v1.1.x 你须要禁用它.

    又一次安装 iOS platform的办法:

    cordova platform remove ios
    cordova platform add ios

    当 platform 被加入之后 - 全部插件会自己主动安装.

    进阶 - 手动移除 Swift 支持. 你须要用Xcode打开 iOS 项目, 然后:

    1. 在 Build Settings,设置 Embedded Content Contains Swift Code 为 NO.

    2. 打开 <YOUR_PROJECT_NAME>-Prefix.pch , 移除 #import <YOUR_PROJECT_NAME>-Swift.h. 比方:

      #ifdef __OBJC__
          #import "TestProject-Swift.h"
      #endif
    3. 又一次build, 检查是否正常.

    Cordova 项目高速向导

    此向导展示了在开发中怎样高速使用这个插件. 我们须要加入 开发扩展 。须要 Xcode 7, 虽然hot code push plugin插件自身能够支持低版本号xcode.

    1. 创建新的Cordova项目。并加入android和iOS platform:

      cordova create TestProject com.example.testproject TestProjectcd ./TestProject
      cordova platform add android
      cordova platform add ios

      或者能够用一个已有的项目.

    2. 加入插件:

      cordova plugin add cordova-hot-code-push-plugin
    3. 加入开发扩展:

      cordova plugin add cordova-hot-code-push-local-dev-addon
    4. 安装 Cordova Hot Code Push 命令行client:

      npm install -g cordova-hot-code-push-cli
    5. 启动本地server:

      cordova-hcp server

      你会看到以下的命令行输出:

      Running server
      Checking:  /Cordova/TestProject/www
      local_url http://localhost:31284
      Warning: .chcpignore does not exist.
      Build 2015.09.02-10.17.48 created in /Cordova/TestProject/www
      cordova-hcp local server available at: http://localhost:31284
      cordova-hcp public server available at: https://5027caf9.ngrok.com
    6. 打开新的控制台, 进入到项目根文件夹,执行app:

      cordova run

      稍等,app会安装到手机或者模拟器.

    7. 如今打开 TestProject/www/index.html , 做一些修改然后保存. 几秒种后你能够在手机或模拟器上看到更新后的页面.

    到此,你能够本地开发。新的web内容会自己主动在设备上更新,而无需又一次启动app查看效果.

    Ionic 项目高速向导

    此向导展示了在开发中怎样高速使用这个插件. 我们须要加入 开发扩展须要 Xcode 7, 虽然hot code push plugin插件自身能够支持低版本号xcode.

    1. 创建新的Ionic项目,并加入android和iOS platform::

      ionic start TestProject blankcd ./TestProject
      ionic platform add android
      ionic platform add ios

      Or use the existing one.

    2. 加入插件:

      ionic plugin add cordova-hot-code-push-plugin
    3. 加入开发扩展:

      ionic plugin add cordova-hot-code-push-local-dev-addon
    4. 安装 Cordova Hot Code Push 命令行client:

      npm install -g cordova-hot-code-push-cli
    5. 启动本地server:

      cordova-hcp server

      你会看到以下的命令行输出:

      Running server
      Checking:  /Cordova/TestProject/www
      local_url http://localhost:31284
      Warning: .chcpignore does not exist.
      Build 2015.09.02-10.17.48 created in /Cordova/TestProject/www
      cordova-hcp local server available at: http://localhost:31284
      cordova-hcp public server available at: https://5027caf9.ngrok.com
    6. 打开新的控制台, 进入到项目根文件夹。执行app:

      ionic run

      稍等。app会安装到手机或者模拟器.

    7. 如今打开 TestProject/www/index.html , 做一些修改然后保存. 几秒种后你能够在手机或模拟器上看到更新后的页面.

    到此,你能够本地开发,新的web内容会自己主动在设备上更新。而无需又一次启动app查看效果.

    更新机制的流程图

    先防止全部的配置相关的内容弄得你稀里糊涂 - 先来看看此插件的实现更新功能的流程图. 应该没有技术细节.


    1. 用户打开你的app.

    2. 插件初始化,在后台进程启动 升级载入器(update loader).

    3. Update loader  从 config.xml 取 config-file 配置(一个url),并从此url载入一段 JSON 配置.  然后它把这段JSON配置中的 release 版本 和当前app 已经安装的进行比較. 假设不同 - 进入下一步.

    4. Update loader 使用app配置(application config)中的 content_url 。去载入清单文件(manifest). 它会找出自上次升级以来,哪些文件须要更新.

    5. Update loader 从 content_url下载更新文件.

    6. 假设一切顺利 - 发出一个"升级文件已经准备好,能够安装了"的通知.

    7. 升级文件已安装, app又一次进入更新过的页面.

    当然, 还有其它的细节, 只是你已经有了大致的思路.

    web内容是怎样存储和更新的

    每个Cordova 项目都有一个 www 文件夹, 这里存放全部的web内容. 当cordova build 运行后 - www 里的内容会复制到相应platform的 www 文件夹下:

    • 安卓: platforms/android/assets/www.

    • iOS: platforms/ios/www.

    于是这些文件被打包进了app. 我们不能更新安装包里的这些文件, 由于它们是仅仅读的. 正由于如此,所以我们要在app第一次启动的时候,将内置的web内容(www文件夹)拷贝到外部存储. 我们不想在拷贝过程中堵塞ui - 我们还是会先载入app内置的index.html. 可是下一次启动或更新 - 我们就从外部存储载入index.html.

    可是假设你的app外壳需要添加新的cordova插件或者原生功能 - 你必需要又一次上架外壳app到store商店. 还有 - 添加外壳 app 的build版本 (App Store 或 Google Play强制的).下次启动,插件检查外壳app版本是否变化, 假设变了 - 会又一次拷贝内置web内容(www文件夹)到外部存储.

    开发app的时候 - 你可能会困惑: 改了一些文件, 又一次启动了app - 但却看到的是旧的页面. 如今你知道原因了: 插件用的是旧版本号的web内容(外部存储中). 若要清除缓存,你须要:

    • 卸载app, 运行 cordova run.

    • 添加外壳app版本,强制插件又一次安装 www 文件夹. 更改外壳app版本请设置 config.xml文件的 android-versionCode 和 ios-CFBundleVersion .

    • 安装 本地开发扩展 ,让它帮你处理版本问题. 每次build他会自己主动帮你app的build版本加1,不须要你手动更改

    上面就是简要介绍, 以便你理解大致的思路. 如今我们继续深入.

    之后你会阅读到 配置文件 这一节- 这有app配置 (application config), 名字是chcp.json. 里面有个 release设置, 这个指明了web内容的版本号. 这个配置必须并且每次公布的release版本号必须不一样. 它由 命令行client 自己主动生成。格式是: yyyy.MM.dd-HH.mm.ss (比方 2015.09.01-13.30.35).

    每次公布,插件在外部存储自己主动生成一个以这个 release版本号 为名字的文件夹, 然后把web内容所有放到这里面. release版本号号成为了 url的一部分. 这个手段能够解决一些问题:

    • 网页内容缓存问题. 比方, iOS 上。css 文件会被 UIWebView缓存起来, 即使我们又一次加载了index.html - 新的样式还是不会被应用. 你须要用任务管理器杀死app, 或者改变css的路径.

    • 基本不会发生更新后损坏已有web内容的现象, 由于我们每次更新都在不同的文件夹下.

    • 即使更新导致了web内容损坏 - 我们能够回滚到上一个版本号的release.

    比方, 我们当前执行的release版本号是 2015.12.01-12.01.33. 这意味着:

    • 全部web内容存储在 /sdcard/some_path/2015.12.01-12.01.33/www/. 包括了Cordova的资源.

    • Index 页面, 用户看到的是 /sdcard/some_path/2015.12.01-12.01.33/www/index.html.

    某个时候我们公布了一个新的release: 2016.01.03-10.45.01. 第一步,插件须要下载新的web文件, 发生情况例如以下:

    1. 在外部存储创建了一个以新的 release 版本为名字的文件夹/sdcard/some_path/2016.01.03-10.45.01/.

    2. 文件夹里面 - 又创建了一个 update 文件夹 : /sdcard/some_path/2016.01.03-10.45.01/update/.

    3. 全部依据 chcp.manifest 更新的文件 都被下载到了这个 update 文件夹内.

    4. 新的 chcp.manifest 和 chcp.json 也被放到了 update 文件夹内.

    5. 新的web内容已准备安装.

    安装更新的时候:

    1. 插件从当前正在使用的release版本号 文件夹内拷贝 www 下全部内容到 新的 release 版本号文件夹下. 用我们的样例就是:从 /sdcard/some_path/2015.12.01-12.01.33/www/ 拷贝全部文件到 /sdcard/some_path/2016.01.03-10.45.01/www/.

    2. update 文件夹下拷贝新的web内容和配置文件,到 www 文件夹下: /sdcard/some_path/2016.01.03-10.45.01/update/ -> /sdcard/some_path/2016.01.03-10.45.01/www/.

    3. 移除 /sdcard/some_path/2016.01.03-10.45.01/update/ 文件夹。由于我们不再使用了.

    4. 载入新的release版本号index.html: /sdcard/some_path/2016.01.03-10.45.01/www/index.html.

    至此。插件会从新的release载入页面, 而旧的release则会作为一个备份留下来,以防万一.

    Cordova Hot Code Push 命令行client

    Cordova Hot Code Push 命令行client 是一个命令行工具,以便你web内容的开发.

    它能够:

    • 生成 chcp.json 和 chcp.manifest 文件, 这样你就不用手动去创建;

    • 执行本地服务,开发时能够检測更新,并公布新的release版本号,使得能够再设备上实时更新web内容;

    • 部署你的web内容到外部server上.

    当然, 你能够不使用这个命令行工具. 仅仅是用了它会更方便一些.

    本地开发扩展

    当你本地开发app时 - 一般做法类似:

    1. web项目做一些更改.

    2. 运行 cordova run 启动app.

    3. 稍等一会查看执行结果.

    即使非常小的变更也须要打包重装app. 耗时比較久,比較麻烦.

    为了提升速度 - 你能够使用本地开发扩展 Hot Code Push Local Development Add-on. 安装非常简答:

    1. 加入此cordova插件.

    2. 启动本地服务 cordova-hcp server.

    3. 在你的项目的config.xml 文件里 <chcp /> 块下加入 <local-development enabled="true" />.

    4. 启动app.

    这样, 全部web内容的变更都会被插件检測到, 并直接更新显示到app上,而不须要重新启动app.

    仅仅有在加入了新的cordova插件时你才会重新启动app.

    重要: 你应该仅仅在开发状态下使用此扩展. 公布外壳app的时候,应该移除此扩展: cordova plugin remove cordova-hot-code-push-local-dev-addon.

    Cordova 配置项

    你应该知道, Cordova 使用 config.xml 文件配置不同项目: app名字, 描写叙述, 起始页面,等等. 使用config.xml文件。你也能够为此插件配置.

    这些配置位于 <chcp> 块. 比方:

    <chcp>
        <config-file url="https://5027caf9.ngrok.com/chcp.json"/>
    </chcp>
    config-file

    定义了一个 URL。指定了须要从哪里载入app配置(application config,就是chcp.json). URL 在 url 属性中声明. 此项必须.

    以防万一,开发的时候, 假设 config-file 未定义 - 会自己主动设为本地服务上 chcp.json 的路径.

    auto-download

    自己主动下载web内容更新. 默认是自己主动, 假设你想手动下载web内容更新,你能够使用 JavaScript 模块(以下有).

    禁用自己主动下载能够设置 config.xml:

    <chcp>
      <auto-download enabled="false" />
    </chcp>

    默认是 true.

    auto-install

    自己主动安装. web内容更新. 默认是自己主动, 假设你想手动安装web内容更新,你能够使用 JavaScript 模块(以下有).

    禁用自己主动安装能够设置 config.xml:

    <chcp>
      <auto-install enabled="false" />
    </chcp>

    默认是 true.

    配置文件

    此插件用到2个配置文件:

    • app配置 Application config - 包括最新的release信息: release 版本, 最低须要的外壳app版本,等等. 文件名称 chcp.json

    • Web内容清单 Content manifest - 包括全部web内容文件的名字和MD5值. 文件名称 chcp.manifest

    这两个文件必须. 他们描写叙述了是否有新的release版本号,以及文件更新时的比較.

    另一个build 可选參数 (build options) 文件, 能够再运行cordova build 命令时指定插件的配置.

    Application config app配置

    包括最新版本号的release信息.

    简单的样例:

    {  "content_url": "https://5027caf9.ngrok.com",  "release": "2015.09.01-13.30.35"}

    这个文件应该放在 www 文件夹下,文件名称是 chcp.json . 这个文件也被打包到了外壳app内.

    你能够手动创建它, 或者用 cordova-hcp 命令(Cordova Hot Code Push 命令行)自己主动生成. 仅仅要在cordova项目根文件夹下执行 cordova-hcp init , 以后要公布新的release仅仅要执行 cordova-hcp build. 很多其它内容请阅读 命令行client的文档.

    content_url

    服务端URL, 也就是你全部web内容文件的位置. 插件会把它作为下载新的清单文件、新的web内容文件的 base url. 此项必须.

    release

    不论什么字符串. 每次release应该唯一. 插件基于这个才知道有没有新版本号release. 此项必须.

    重要: 插件仅仅比較release字符串是否相等, 假设不等,就觉得服务端有新版本号.(不会比較大小)

    min_native_interface

    所需最小的外壳app版本号. 这是app的build版本号号。是个整型数字, 不是应用商店中看到的形如"1.0.0"字符串.

    在 config.xml中。这样指定build版本:

    <widget id="io.cordova.hellocordova"
          version="1.0.1"
          android-versionCode="7"
          ios-CFBundleVersion="3">
    • version - app字符串版本号号, 也就是用户在商店中看到的版本号.

    • android-versionCode - 安卓的build版本. 这个应该用于 min_native_interface.

    • ios-CFBundleVersion - iOS的build版本.这个应该用于 min_native_interface.

    Preference creates dependency between the web and the native versions of the application.

    重要: 由于cordova的一个奇葩现象, 生成的 .apk 的build版本会被加 10, 导致了变成了形如 70, 72, or 74, 依据不同平台 (arm/x86/etc),后面的0、2、4不一样. 为了绕过这个, 我们建议也给 iOS build版本手动加10, 这样 min_native_interface (比方 70) 就能够对安卓和iOS都有效, 大致是这样:

    <widget id="io.cordova.hellocordova"
          version="1.0.1"
          android-versionCode="7"
          ios-CFBundleVersion="70">

    举个样例, 如果你的外壳app加了个新的插件 - 你应该会更新外壳app. 为了防止用户下载了不适合他现有外壳app的web内容 - 你应该设置 min_native_interface 这个值.

    比方, 我们app里的chcp.json是这种:

    {  "content_url": "https://5027caf9.ngrok.com",  "release": "2015.09.01-13.30.35",  "min_native_interface": 10}

    外壳app的build版本号是 13.

    某个时候,web内容有了新的release公布:

    {  "content_url": "https://5027caf9.ngrok.com",  "release": "2015.09.05-12.20.15",  "min_native_interface": 15}

    插件载入到这段json的时候, 发现 min_native_interface 比当前外壳app的build号要大 - 它就不会下载web内容. 而是触发一个 chcp_updateLoadFailed 错误通知, 告诉用户须要升级外壳app了. 很多其它内容请看 从应用商店请求app升级  小节.

    备注: 眼下你还不能为不同平台指定不同的 min_native_interface . 假设须要以后能够支持.

    update

    指定了什么时候安装web内容更新. 支持的值有:

    • start - app启动时安装更新. 默认值.

    • resume - app从后台切换过来的时候安装更新.

    • now - web内容完成下载即安装更新.

    你能够用JavaScript禁止自己主动安装. 请看 JavaScript module 小节.

    android_identifier

    apk包名. 假设指定了 - 引导用户到 Google Play Store 的app页面.

    ios_identifier

    ios应用标识号, 比方: id345038631. 假设指定了 - 引导用户到 App Store 的app页面.

    Content manifest内容清单

    内容清单描写叙述了web项目全部文件的状态.

    [
      {    "file": "index.html",    "hash": "5540bd44cbcb967efef932bc8381f886"
      },
      {    "file": "css/index.css",    "hash": "e46d9a1c456a9c913ca10f3c16d50000"
      },
      {    "file": "img/logo.png",    "hash": "7e34c95ac701f8cd9f793586b9df2156"
      },
      {    "file": "js/index.js",    "hash": "0ba83df8459288fd1fa1576465163ff5"
      }
    ]

    依据它,插件才知道什么文件被移除了, 什么文件更新或新增了. 于是:

    • 更新阶段。从服务端下载全部web内容文件;

    • 安装阶段,删除服务端不存在(已移除)的文件.

    这个文件应该放在 www 文件夹下,文件名称是 chcp.manifest .这个文件也被打包到了外壳app内.

    相同的, 清单文件要放到 content_url (app配置 Application config中指定的)指定的文件夹下. 比方, 假设你的 content_url 是 https://somedomain.com/www, 这个清单文件的url就必须是 https://somedomain.com/www/chcp.manifest.

    生成 chcp.manifest 文件能够运行命令行client的 build 命令 (在cordova项目根文件夹下运行):

    cordova-hcp build
    file

    相对于 www 的路径(就是你存放web内容的地方).

    比方, 你的web内容位于:  /Workspace/Cordova/TestProject/www.  你的 file 值应该是相对于这个路径.

    hash

    文件的 MD5 值. 用于检測自上次release以来。这个文件是否变更过. 还实用于检測app端下载的文件是否出错.

    建议: 每次变更web内容后都应该更新 chcp.manifest 文件. 否则插件不会检測到不论什么更新.

    Build options build设置

    就像在 Cordova 配置项 一节中说的 - 你能够在config.xml 文件中改变插件配置.

    可是假设你想在使用build命令行的时候改变插件配置呢? 为了达到这个目的,你须要使用chcpbuild.options 文件.

    文件必须位于 Cordova 项目根文件夹. 在这个文件中面。你指定(JSON格式) 全部你想改变 config.xml 文件的配置. 源文件 config.xml (Cordova项目根文件夹) 不会发生变动, 我们改变的是 特定平台下的 config.xml  (在cordova build过程的 after_prepare 阶段).

    比方, 你的Cordova项目是 /Cordova/TestProject 文件夹.config.xml 文件 (/Cordova/TestProject/config.xml) 有以下的配置:

    <chcp>
      <config-file url="https://company_server.com/mobile/www/chcp.json" />
    </chcp>

    这时我们在 /Cordova/Testproject/ 下创建 chcpbuild.options 文件,文件内容例如以下:

    {
      "dev": {
        "config-file": "https://dev.company_server.com/mobile/www/chcp.json"
      },
      "production": {
        "config-file": "https://company_server.com/mobile/www/chcp.json"
      },
      "QA": {
        "config-file": "https://test.company_server.com/mobile/www/chcp.json"
      }
    }

    build app的时候, 转为开发要用的server, 可运行:

    cordova build -- chcp-dev

    结果就是, 特定拍下的 config.xml 文件(比方, /Cordova/TestProject/platforms/android/res/xml/config.xml) 变成了这样:

    <chcp>
      <config-file url="https://dev.company_server.com/mobile/www/chcp.json"/>
    </chcp>

    你可能注意到了 - 我们用的命令有个 chcp-. 这个必须, 这样插件才知道, 这个參数是为它设置的. 并且, 不会和其他插件的命令參数冲突.

    假设你的app能够測试了 - 你能够用以下的命令build, 就指定了測试server:

    cordova build -- chcp-QA

    特定平台下的 config.xml 就会变成:

    <chcp>
      <config-file url="https://test.company_server.com/mobile/www/chcp.json"/>
    </chcp>

    当我们须要上架app的时候 (Google Play, App Store) - 我们正常build:

    cordova build --release

    这样 config.xml 是不会改变的.

    假设没有使用 chcpbuild.options  - 插件会使用 config.xml 面默认的值.

    JavaScript 模块

    默认情况下, 全部的 检查更新->下载->安装 过程都是插件在原生端自己主动进行的. 不须要其他js端代码. 然而, 这些过程也能够用js控制.

    你能够:

    • 监听更新相关的事件;

    • 从服务端检查和下载新的web内容;

    • 安装已下载的web内容;

    • 更改插件配置;

    • 让用户到应用商店下载新的外壳app.

    监听更新事件

    比方, web内容已经下载并能够安装了。会有事件通知, 或者出错了导致安装新的web内容失败了.

    监听事件像这样:

      document.addEventListener(eventName, eventCallback, false);
    
      function eventCallback(eventData) {
        // do something
      }

    错误事件有具体错误信息. 像这样:

    function eventCallback(eventData) {
      var error = eventData.details.error;
      if (error) {
        console.log('Error with code: ' + error.code);
        console.log('Description: ' + error.description);
      }
    }

    可用的事件例如以下:

    • chcp_updateIsReadyToInstall - web内容已经下载并能够安装时触发.

    • chcp_updateLoadFailed - 插件无法下载web更新时触发. 具体错误信息在事件參数里.

    • chcp_nothingToUpdate - 无可用更新下载时触发.

    • chcp_updateInstalled - web内容成功安装时触发.

    • chcp_updateInstallFailed - web内容安装失败时触发. 具体错误信息在事件參数里.

    • chcp_nothingToInstall -无可用更新安装时触发.

    • chcp_assetsInstalledOnExternalStorage - 插件成功把app内置的web内容复制到外置存储中时触发. 你可能须要开发调试时用到这个事件。或许不会.

    • chcp_assetsInstallationError -插件无法拷贝app内置的web内容到外置存储中时触发. 假设此事件发生了 - 插件不再工作. 或许是设备没有足够的存储空间导致.  具体错误信息在事件參数里.

    该举一些简单的样例了. 如果我们有个 index.js 文件, 它被 index.html引用.

    var app = {
    
      // Application Constructor
      initialize: function() {
        this.bindEvents();
      },
    
      // Bind any events that are required.
      // Usually you should subscribe on 'deviceready' event to know, when you can start calling cordova modules
      bindEvents: function() {
        document.addEventListener('deviceready', this.onDeviceReady, false);
      },
    
      // deviceready Event Handler
      onDeviceReady: function() {
        console.log('Device is ready for work');
      }
    };
    
    app.initialize();

    这个和cordova默认创建的 index.js 文件非常像. 监听 chcp_updateIsReadyToInstall 事件例如以下:

    bindEvents: function() {
      // ...some other events subscription code...
    
      document.addEventListener('chcp_updateIsReadyToInstall', this.onUpdateReady, false);
    },

    编写事件处理函数:

    // chcp_updateIsReadyToInstall Event Handler
    onUpdateReady: function() {
      console.log('Update is ready for installation');
    }

     index.js 结果例如以下:

    var app = {
    
      // Application Constructor
      initialize: function() {
        this.bindEvents();
      },
    
      // Bind any events that are required.
      // Usually you should subscribe on 'deviceready' event to know, when you can start calling cordova modules
      bindEvents: function() {
        document.addEventListener('deviceready', this.onDeviceReady, false);
        document.addEventListener('chcp_updateIsReadyToInstall', this.onUpdateReady, false);
      },
    
      // deviceready Event Handler
      onDeviceReady: function() {
        console.log('Device is ready for work');
      },
    
      // chcp_updateIsReadyToInstall Event Handler
      onUpdateReady: function() {
        console.log('Update is ready for installation');
      }
    };
    
    app.initialize();

    这样我们就知道了web内容什么时候完成下载并能够安装了. 通过 JavaScript 模块我们能够让插件即时安装web更新, 否则将在下次启动app时安装.

    检查更新

    使用js代码。让插件检查更新:

    chcp.fetchUpdate(updateCallback);
    
    function updateCallback(error, data) {
      // do some work
    }

    回调有2个參数:

    • error - 假设检查失败,有error參数; null 表示一切正常;

    • data - 额外的 数据, 原生端提供. 临时能够忽略.

    我们如果 index.html 有一些button, 按下它能够检查更新. 我们须要这样写代码:

    1. 监听button的 click 事件.

    2. 当点击button时调用chcp.fetchUpdate() .

    3. 处理更新事件的结果.

    我们来改 index.js 代码:

    var app = {
    
      // Application Constructor
      initialize: function() {
        this.bindEvents();
      },
    
      // Bind any events that are required.
      // Usually you should subscribe on 'deviceready' event to know, when you can start calling cordova modules
      bindEvents: function() {
        document.addEventListener('deviceready', this.onDeviceReady, false);
      },
    
      // deviceready Event Handler
      onDeviceReady: function() {
        // Add click event listener for our update button.
        // We do this here, because at this point Cordova modules are initialized.
        // Before that chcp is undefined.
        document.getElementById('myFetchBtn').addEventListener('click', app.checkForUpdate);
      },
    
      checkForUpdate: function() {
        chcp.fetchUpdate(this.fetchUpdateCallback);
      },
    
      fetchUpdateCallback: function(error, data) {
        if (error) {
          console.log('Failed to load the update with error code: ' + error.code);
          console.log(error.description);
        } else {
          console.log('Update is loaded');
        }
      }
    };
    
    app.initialize();

    注意: 即使你在fetchUpdate 回调里处理了,相关的更新事件还是会触发并广播的.

    安装web更新

    调用:

    chcp.installUpdate(installationCallback);
    
    function installationCallback(error) {
      // do some work
    }

    假设安装失败 - error 參数会有错误具体信息. 否则- 为 null.

    如今让我们来继续上面的代码,处理web内容下载完后的安装.

    var app = {
    
      // Application Constructor
      initialize: function() {
        this.bindEvents();
      },
    
      // Bind any events that are required.
      // Usually you should subscribe on 'deviceready' event to know, when you can start calling cordova modules
      bindEvents: function() {
        document.addEventListener('deviceready', this.onDeviceReady, false);
      },
    
      // deviceready Event Handler
      onDeviceReady: function() {
        // Add click event listener for our update button.
        // We do this here, because at this point Cordova modules are initialized.
        // Before that chcp is undefined.
        document.getElementById('myFetchBtn').addEventListener('click', app.checkForUpdate);
      },
    
      checkForUpdate: function() {
        chcp.fetchUpdate(this.fetchUpdateCallback);
      },
    
      fetchUpdateCallback: function(error, data) {
        if (error) {
          console.log('Failed to load the update with error code: ' + error.code);
          console.log(error.description);
          return;
        }
        console.log('Update is loaded, running the installation');
    
        chcp.installUpdate(this.installationCallback);
      },
    
      installationCallback: function(error) {
        if (error) {
          console.log('Failed to install the update with error code: ' + error.code);
          console.log(error.description);
        } else {
          console.log('Update installed!');
        }
      }
    };
    
    app.initialize();

    注意: 即使你在 installUpdate 回调里处理了,相关的更新事件还是会触发并广播的

    执行时改变插件设置

    正常情况下,全部的插件配置都在 config.xml. 可是你能够用js动态改变.

    通过以下的代码实现:

    chcp.configure(options, callback);
    
    function callback(error) {
      // do some work
    }

    支持的有:

    • config-file - application config(chcp.json) 的url. 假设设置了 - 这个url将会被用于检查更新,而不是config.xml中的值.

    • auto-download - 设为 false 你能够禁止插件自己主动检測web内容更新并下载.

    • auto-install - 设为 false 你能够禁止插件自己主动安装web更新.

    这些须要在 deviceready 事件中设置. 你应该在每一个页面载入的时候处理, 

    假如你一开就打算手动更新和下载安装 - 你应该在config.xml中设置

    <chcp>
      <auto-download enabled="false" />
      <auto-install enabled="false" />
    </chcp>

    而不是js端动态设置.

    比方, 我们在config.xml禁用了 auto-download and auto-install  . 然后某个时间点 config-file 改变了, 可是我们不想从原有的url检測和下载web更新. 此时, 我们应该这样:

    1. 公布新版本号的web内容, 它们能够用于最初的 config-file url.

    2. 在新的版本号 index.js 文件里,内容像这样:

      var app = {
      
        // Application Constructor
        initialize: function() {
          this.bindEvents();
        },
      
        // Bind any events that are required.
        // Usually you should subscribe on 'deviceready' event to know, when you can start calling cordova modules
        bindEvents: function() {
          document.addEventListener('deviceready', this.onDeviceReady, false);
        },
      
        // deviceready Event Handler
        onDeviceReady: function() {
          // change plugin options
          app.configurePlugin();
        },
      
        configurePlugin: function() {
          var options = {
            'config-file': 'https://mynewdomain.com/some/path/mobile/chcp.json'
          };
      
          chcp.configure(options, configureCallback);
        },
      
        configureCallback: function(error) {
          if (error) {
            console.log('Error during the configuration process');
            console.log(error.description);
          } else {
            console.log('Plugin configured successfully');
            app.checkForUpdate();
          }
        },
      
        checkForUpdate: function() {
          chcp.fetchUpdate(this.fetchUpdateCallback);
        },
      
        fetchUpdateCallback: function(error, data) {
          if (error) {
            console.log('Failed to load the update with error code: ' + error.code);
            console.log(error.description);
            return;
          }
          console.log('Update is loaded, running the installation');
      
          chcp.installUpdate(this.installationCallback);
        },
      
        installationCallback: function(error) {
          if (error) {
            console.log('Failed to install the update with error code: ' + error.code);
            console.log(error.description);
          } else {
            console.log('Update installed!');
          }
        }
      };
      
      app.initialize();


    引导用户去应用商店更新外壳app

    从 Application config app配置 小节我们知道。能够给web更新设置最小支持的外壳app版本号 (min_native_interface ). 假设插件检查发现用户安装的外壳app版本号比服务端新的web内容要求的版本号要低 - 就会触发错误事件。错误码chcp.error.APPLICATION_BUILD_VERSION_TOO_LOW. 通过这个错误码我们能够引导用户去应用商店更新外壳app (Google Play /App Store).

    这里你想怎么做就怎么做. 经常用法是显示一个对话框,问用户是否须要转到应用商店. 插件也提供了这个.

    你须要做的是:

    1. 在 application config(chcp.json) 中设置t android_identifier 和  ios_identifier.

    2. js端监听对应事件,并在出现错误的时候调用 chcp.requestApplicationUpdate 方法.

    举个样例. 简单起见我们监听 chcp_updateLoadFailed 事件.

    var app = {
    
      // Application Constructor
      initialize: function() {
        this.bindEvents();
      },
    
      // Bind any events that are required.
      // Usually you should subscribe on 'deviceready' event to know, when you can start calling cordova modules
      bindEvents: function() {
        document.addEventListener('deviceready', this.onDeviceReady, false);
        document.addEventListener('chcp_updateLoadFailed', this.onUpdateLoadError, false);
      },
    
      // deviceready Event Handler
      onDeviceReady: function() {
      },
    
      onUpdateLoadError: function(eventData) {
        var error = eventData.detail.error;
        if (error && error.code == chcp.error.APPLICATION_BUILD_VERSION_TOO_LOW) {
            console.log('Native side update required');
            var dialogMessage = 'New version of the application is available on the store. Please, update.';
            chcp.requestApplicationUpdate(dialogMessage, this.userWentToStoreCallback, this.userDeclinedRedirectCallback);
        }
      },
    
      userWentToStoreCallback: function() {
        // user went to the store from the dialog
      },
    
      userDeclinedRedirectCallback: function() {
        // User didn't want to leave the app.
        // Maybe he will update later.
      }
    };
    
    app.initialize();

    错误码

    下载安装web更新的过程中可能会发生一些错误. 你能够从回调或者事件中匹配错误码( chcp.error 对象中有各种错误码).

    v1.2.0版本号之前 你须要用特定的错误码数字值. 此时開始, 请使用静态常量名,这样能够使代码可读。也能够降低对错误码详细数字的依赖. 比方, 不应该用 if (error.code == -2) 而用 if (error.code == chcp.error.APPLICATION_BUILD_VERSION_TOO_LOW).

    错误列表:

    • NOTHING_TO_INSTALL - 请求插件安装更新。却没有更新须要安装. 值为 1.

    • NOTHING_TO_UPDATE - 没有可用web更新须要下载.值为 2.

    • FAILED_TO_DOWNLOAD_APPLICATION_CONFIG - 下载新的application config 文件(chcp.json)失败. 要么文件不存在或者网络问题.值为 -1.

    • APPLICATION_BUILD_VERSION_TOO_LOW - 外壳app的build版本太低. 新的web内容须要新的外壳app. 用户须要更新外壳app.值为 -2.

    • FAILED_TO_DOWNLOAD_CONTENT_MANIFEST - 下载内容清单文件(chcp.manifest)失败. 文件chcp.manifest 必须位于 content_url 相应文件夹下, 和chcp.json一起.值为 -3.

    • FAILED_TO_DOWNLOAD_UPDATE_FILES - 下载web内容失败. 清单 chcp.manifest 中列出文件的必须都要位于 content_url 相应文件夹下. 还有, 检查各个文件的MD5是否正确. 值为 -4.

    • FAILED_TO_MOVE_LOADED_FILES_TO_INSTALLATION_FOLDER - 移动已下载的文件到安装文件夹时失败. 可能存储空间不足.值为 -5.

    • UPDATE_IS_INVALID - web内容已损坏. 安装之前。插件会检查已下载文件的MD5和 chcp.manifest 中的比較看是否一致. 假设不一致或者文件缺失 - 会发生此错误. 值为 -6.

    • FAILED_TO_COPY_FILES_FROM_PREVIOUS_RELEASE - 从上一版本号拷贝www下文件到新版本号www文件夹出错.可能存储空间不足.值为 -7.

    • FAILED_TO_COPY_NEW_CONTENT_FILES - 拷贝新文件到内容文件夹下失败.可能存储空间不足.值为 -8.

    • LOCAL_VERSION_OF_APPLICATION_CONFIG_NOT_FOUND - 载入本地chcp.json失败. 可能是用户手动删除了外部存储的web内容相关文件. 假设发生。会回滚至上移release版本号的web内容.值为 -9.

    • LOCAL_VERSION_OF_MANIFEST_NOT_FOUND -载入本地chcp.manifest失败.可能是用户手动删除了外部存储的web内容相关文件. 假设发生。会回滚至上移release版本号的web内容. 值为 -10.

    • LOADED_VERSION_OF_APPLICATION_CONFIG_NOT_FOUND -载入本地已下载的新版本号的chcp.json失败.可能是用户手动删除了外部存储的web内容相关文件.假设发生 - app下次启动时会恢复. 值为 -11.

    • LOADED_VERSION_OF_MANIFEST_NOT_FOUND -载入本地已下载的新版本号的chcp.manifest失败.可能是用户手动删除了外部存储的web内容相关文件.假设发生 - app下次启动时会恢复.值为 -12.

    • FAILED_TO_INSTALL_ASSETS_ON_EXTERNAL_STORAGE - 拷贝app内置web内容到外部存储时失败.可能存储空间不足. app初次启动时会运行此操作. 假设失败。插件就不再实用了. 值为 -13.

    • CANT_INSTALL_WHILE_DOWNLOAD_IN_PROGRESS - 调用 chcp.installUpdate 而 插件正在下载更新时触发. 你必须等待完成下载. 值为 -14.

    • CANT_DOWNLOAD_UPDATE_WHILE_INSTALLATION_IN_PROGRESS - 调用 chcp.fetchUpdate 而安装过程在再运行. 你必须等待安装完成. 值为 -15.

    • INSTALLATION_ALREADY_IN_PROGRESS - 调用 chcp.installUpdate,而安装过程在再运行.值为 -16.

    • DOWNLOAD_ALREADY_IN_PROGRESS - 调用 chcp.fetchUpdate,而 插件正在下载更新时触发. 值为 -17.

    • ASSETS_FOLDER_IN_NOT_YET_INSTALLED - 调用 chcp 方法, 而插件正在拷贝app内置web内容到外部存储时触发. 仅仅可能在app初次启动时发生. 最后这个错误会被移除.值为 -18.




    关于热更新的流程解析

    好多同学都測试不成功。大家不要想太复杂了。我再简要概括一下:

    1. chcp.json文件里的content_url为server项目的地址加port号

    2. config.xml为server项目地址加port号再加上/chcp.json

    3. 每次改动完文件后。必须将【改动的文件】和【chcp.manifest文件】一并拷贝到server项目中进行覆盖。

    4. 将server中的chcp.json文件里的【"release": "2016.08.04-18.04.06"】时间改为当前时间。

    3 和 4是最重要的。不然热更新就不起作用。

    最后你们不要在纠结cordova-hcp server,这个东西就是在开发的时候启动用来监听文件的改动。假设有文件改动。就相应在chcp.manifest中改动该文件的hash值。

    还没完。为了更清楚的了解热更新是怎么回事,这里我画了一张图。

    [热更新的流程解析]


    1. app启动

    2. 从server请求chcp.json文件(会覆盖本地chcp.json文件)。

    3. server返回chcp.json文件与app里的chcp.json文件做对照,推断两个文件里的release时间。

    4. 假设serverchcp.json文件的release时间大于app里chcp.json的release时间(说明新的资源)

    5. 假设有新的资源。再次发送一个请求,请求server的chcp.manifest文件(会覆盖本地chcp.json文件)。

    6. server返回chcp.manifest文件与app里的chcp.manifest文件内容做对照。

    7. 假设有不一样的hash值。

    8. 对server请求新的资源。

    9. 请求成功的资源将覆盖本地资源。


    案例

    这里通过对app进行抓包,来分析热更新是如何进行应用内更新的。注意看1~8。我是没有对server资源进行更新的。直到第9个请求的时候:9。10,11连续发送了3个请求。

    [热更新的抓包图]

    • 第9个请求将server的chcp.json文件请求回来后推断时间是大于app的chcp.json时间的

    • 然后发送了第10个请求。chcp.manifest,与本地chcp.manifest文件做对照

    • 当中我仅仅改了一个login.html,所以这里对login.html又一次载入覆盖。



    一直有人问我,用不了。不会用之类的。我在这说一下

    用 cordova-hcp server 命令启动 hcp 服务器的时候,会看到如上图的local serverpublic server,这两个地址是不能自定义

    类似“https://f5f6894c.ngrok.io” 这个地址貌似并没实用,或许仅仅是国内没法用吧
    所以。我建议不要用 cordova-hcp server,你须要自己部署一个服务器,托管 www 下的 web 内容
    Local Development Add-on 这个扩展也能够不要,记得自己生成新的apk/ipa之前要改config.xml的version(android-versionCode/ios-CFBundleVersion)即可了

    事实上 cordova-hcp server 启动的那个服务器。就是多了2个功能:
    1、检測到 www 变更后。自己主动生成清单文件 chcp.manifest
    2、自己主动实时推送变更到app端

    用了你自己的server之后,这2个功能都没了。所以
    1、每次更改 www 下的 web 内容之后,一定要手动用 cordova-hcp build(在corodva项目根文件夹下运行), 生成清单文件 chcp.manifest
    2、app 仅仅能在每次启动的时候。才干检查有无内容更新。有更新就会在后台下载。等到下次启动 app 才应用更新。

    (也就是要重新启动app 2次才干看到效果)

    chcp.json 这个文件的内容也要改下,把 update 改为 "start",比方

    {
      "update": "start",
      "content_url": "http://10.0.0.100/HCP/",
      "release": "2016.04.28-10.14.32"
    }

    chcp.json 的功能,不懂的看上面翻译


    能够在 cordova 项目根文件夹下放一个 cordova-hcp.json,这是个模板文件
    这样每次运行 cordova-hcp build, 就会利用这个模板生成新的 chcp.json,而不用手动更改 www/chcp.json了。
    cordova-hcp.json内容例如以下:
    {
      "update": "start",
      "content_url": "http://10.0.0.100/HCP/"
    }




    常见问题解决的方法:

    1、假设用安卓模拟器測试的,请确保手机模拟器的时区、时间和server时区、时间一致。否则。版本对不上。就检測不到更新了








    欢迎增加Sencha Touch + Phonegap交流群

    1群:194182999 (满)

    2群:419834979

    共同学习交流(博主QQ:479858761

  • 相关阅读:
    密码学复习
    Kafka Stream 高级应用
    Kafka Stream 流和状态
    Kafka Stream 处理器API
    SSM工作流程与原理详解
    Seata AT和XA模式
    分布式锁结合SpringCache
    使用RabbitMQ最终一致性库存解锁
    使用Springboot+SpringCloud+Seata1.3.0+Nacos1.2.1进行全局事务管理
    在微服务环境下,远程调用feign和异步线程存在请求数据丢失问题
  • 原文地址:https://www.cnblogs.com/gccbuaa/p/7141974.html
Copyright © 2020-2023  润新知