[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 项目高速向导

  • Ionic 项目 高速向导

  • 更新机制的流程图

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

  • Cordova Hot Code Push 命令行客户端

  • 本地开发扩展

  • Cordova 配置项

  • 配置文件

    • Application config app配置

    • Content manifest 内容清单

    • Build options build设置

  • JavaScript 模块

    • 监听更新的事件

    • 请求更新

    • 安装更新

    • 执行时改变插件设置

    • 请求从store更新app(外壳)

  • 错误码

安装

须要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 Constructorinitialize: function() {this.bindEvents();},// Bind any events that are required.// Usually you should subscribe on 'deviceready' event to know, when you can start calling cordova modulesbindEvents: function() {document.addEventListener('deviceready', this.onDeviceReady, false);},// deviceready Event HandleronDeviceReady: 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 Constructorinitialize: function() {this.bindEvents();},// Bind any events that are required.// Usually you should subscribe on 'deviceready' event to know, when you can start calling cordova modulesbindEvents: function() {document.addEventListener('deviceready', this.onDeviceReady, false);document.addEventListener('chcp_updateIsReadyToInstall', this.onUpdateReady, false);},// deviceready Event HandleronDeviceReady: function() {console.log('Device is ready for work');},// chcp_updateIsReadyToInstall Event HandleronUpdateReady: 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 Constructorinitialize: function() {this.bindEvents();},// Bind any events that are required.// Usually you should subscribe on 'deviceready' event to know, when you can start calling cordova modulesbindEvents: function() {document.addEventListener('deviceready', this.onDeviceReady, false);},// deviceready Event HandleronDeviceReady: 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 Constructorinitialize: function() {this.bindEvents();},// Bind any events that are required.// Usually you should subscribe on 'deviceready' event to know, when you can start calling cordova modulesbindEvents: function() {document.addEventListener('deviceready', this.onDeviceReady, false);},// deviceready Event HandleronDeviceReady: 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 Constructorinitialize: function() {this.bindEvents();},// Bind any events that are required.// Usually you should subscribe on 'deviceready' event to know, when you can start calling cordova modulesbindEvents: function() {document.addEventListener('deviceready', this.onDeviceReady, false);},// deviceready Event HandleronDeviceReady: function() {// change plugin optionsapp.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 Constructorinitialize: function() {this.bindEvents();},// Bind any events that are required.// Usually you should subscribe on 'deviceready' event to know, when you can start calling cordova modulesbindEvents: function() {document.addEventListener('deviceready', this.onDeviceReady, false);document.addEventListener('chcp_updateLoadFailed', this.onUpdateLoadError, false);},// deviceready Event HandleronDeviceReady: 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)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/540940.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java 如何重写迭代器,如何用Java按需定制自己的迭代器

编写自己的迭代器的流程是&#xff1a;首先实现Iterable接口&#xff0c;进而实现该接口中的Iterator iterator()方法&#xff0c;该方法返回接口Iterator&#xff0c;Iterator接口中封装了next&#xff0c;hasnext&#xff0c;remove等方法。实现了Iterable接口的类能够通过fo…

php整合支付宝,Thinkphp5.0整合支付宝在线下单

thinkphp5.0支付宝在线支付下单整个流程&#xff0c;包括创建订单、支付成功回调更新订单状态、最终跳转到商户订单详情页查看演示下载资源&#xff1a;17次 下载资源下载积分&#xff1a;998积分支付宝在线支付控制器代码 public function alipay() {//发起支付宝支付$order_n…

php怎么引用表单元素,表单元素:最全的各种html表单元素获取和使用方法总结...

表单是网页与用户的交互工具&#xff0c;由一个元素作为容器构成&#xff0c;封装其他任何数量的表单控件&#xff0c;还有其他任何元素里可用的标签&#xff0c;表单能够包含、、、、、等表单控件元素。表单元素有哪些呢&#xff1f;它包含了如下的这些元素&#xff0c;输入文…

数据中心部署气流遏制系统需要考虑的十大要素

数据中心气流遏制策略能够大幅提高传统数据中心制冷系统的可预测性和效率。事实上&#xff0c;绿色网格组织&#xff08;The Green Grid&#xff09;将气流管理策略称作“实施数据中心节能计划的起点”。但是&#xff0c;大多数已有数据中心由于受各种条件的制约&#xff0c;只…

JAVA语言异常,Java语言中的异常

1、异常分类从产生源头来看&#xff0c;Java语言中的异常可以分为两类&#xff1a;JVM抛出的异常。比如&#xff1a;访问null引用会引发NullPointerException&#xff1b;0作为除数&#xff0c;如9/0&#xff0c;JVM会抛出ArithmeticException&#xff1b;内存消耗完&#xff0…

使用Mybatis Generator结合Ant脚本快速自动生成Model、Mapper等文件的方法

新建generatorConfig.xml和build_mybatis.xml&#xff1a; jar下载 <dependency><groupId>org.mybatis.generator</groupId><artifactId>mybatis-generator-core</artifactId><version>1.3.2</version></dependency> <depe…

STM32启动解析

启动方式对的不同下载模式 STM32可以通过BOOT引脚的配置&#xff0c;来选择不同的启动模式------对应不同的下载方式。 仿真器下载—— 内部FLASH的启动方式 串口下载 —— 系统存储器的启动方式 内部SRAM一般不用&#xff0c;不讲 启动过程 以内部FLASH的启动方式为例&am…

自动化部署kvm虚拟机_自动化虚拟助手

自动化部署kvm虚拟机The automated virtual assistant or commonly called personal assistants, are developed to serve its users by performing some tasks, setting reminders and much more based on the input is given and local awareness. It is integrated with a l…

php 数据库编码,php怎么设置数据库编码方式

在php中&#xff0c;可以使用mysql_query()函数来设置mysql数据库的编码方式&#xff1b;具体方法&#xff1a;在mysql_connect()语句之后添加“mysql_query("set names 编码方式");”代码即可。本教程操作环境&#xff1a;windows7系统、PHP7.1版&#xff0c;DELL G…

mysql截取字符串与reverse函数

mysql的函数大全&#xff1a; http://www.jb51.net/Special/606.htm 这个网页上很多知识点&#xff0c;可以学习下&#xff0c;关于mysql的函数&#xff0c;也可以作为API查询&#xff1a; 这里只说下mysql的截取函数和reverse函数&#xff1a; MySQL 字符串截取函数&#xff1…

flask sql外键使用_如何在SQL中使用外键?

flask sql外键使用Basically, Foreign Key represents relationship between tables. 基本上&#xff0c; 外键代表表之间的关系 。 Syntax: 句法&#xff1a; column-name data_type (size) CONSTRAINT constraint-name References Table-name (Column-name)Example: 例&a…

合并排序算法排序过程_外部合并排序算法

合并排序算法排序过程外部分类 (External sorting) External sorting is a technique in which the data is stored on the secondary memory, in which part by part data is loaded into the main memory and then sorting can be done over there. Then this sorted data wi…

php榛子云短信验证,java + maven +榛子云短信 实现发送短信验证码功能

如何使用java maven的项目环境发送短信验证码&#xff0c;本文使用的是榛子云短信的接口。下载下来是jar文件&#xff0c;需要将jar发布到本地的maven仓库中&#xff0c; 在cmd环境下输入:mvn install:install-file -DgroupIdcom.zhenzi -DartifactIdsms -Dversion1.0.0 -Dpac…

django css_在应用程序上实现CSS Django的

django cssCSS (Cascade Style Sheets) are used to implement design. CSS(级联样式表)用于实现设计。 Step 1: Create a Sandbox, Activate it, Install Django and Create Sample Project 步骤1&#xff1a;创建一个沙箱&#xff0c;将其激活&#xff0c;安装Django并创建示…

vb mysql 表格显示,在VB中编辑数据库和电子表格

在VB50中有很多功能强大的控件&#xff0c;其中数据控件与一些绑定控件(如文本框&#xff0c;图片框及 ActiveX控件)的相互协作&#xff0c;能够方便地实现对各种数据库记录、表格乃至电子表格的浏览和编辑操作。下面介绍实例&#xff0c;其中数据控件用于记录的浏览、移动、…

[转]Visual Studio 各版本下载

原文地址&#xff1a;[置顶] Visual Studio 各版本下载 文件名称文件大小百度网盘下载微软官方下载Visual Studio 2015 Enterprise - 企业版 - 简体中文3.89GBhttp://pan.baidu.com/s/1bnAY68Bvs2015.ent_chs.isoVisual Studio 2015 Professional - 专业版 - 简体中文3.84GBht…

oracle 列级外键,Oracle外键列上是否需要索引?

外键列上缺少索引会带来两个问题&#xff0c;限制并发性、影响性能。而这两个问题中的任意一个都可能会造成严重性能问题。 无论是Or外键列上缺少索引会带来两个问题&#xff0c;限制并发性、影响性能。而这两个问题中的任意一个都可能会造成严重性能问题。无论是Oracle的官方文…

python 改变词典顺序_按词典顺序排列的功率集

python 改变词典顺序Description: 描述&#xff1a; This is a standard interview problem to find out the power sets in lexicographic order of a given set of numbers using backtracking. 这是一个标准的面试问题&#xff0c;它使用回溯来按给定数字集的字典顺序查找能…

springJdbc in 查询,Spring namedParameterJdbcTemplate in查询

springJdbc in 查询&#xff0c;Spring namedParameterJdbcTemplate in查询&#xff0c; SpringJdbc命名参数in查询&#xff0c;namedParameterJdbcTemplate in查询 >>>>>>>>>>>>>>>>>>>>>>>>>>…

python 添加图例_Python | 在图例标签中添加Sigma

python 添加图例Sigma (&#x1d70e;) is very often used greek mathematical letters and has a higher repetition in probability. In this article, we are going to add &#x1d70e; using a command in matplotlib. Sigma(&#x1d70e;)是希腊数学字母中经常使用的字…