首页 > 代码库 > [Phonegap+Sencha Touch] 移动开发77 Cordova Hot Code Push插件实现自己主动更新App的Web内容
[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 代码, 图片等等. 一般有两种方式进行升级:
在appstore中上架新的app. 可是耗时比較长.
牺牲全部原生功能。每次打开都从远端站点载入. 可是假设没有网络,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 项目, 然后:
在
Build Settings
,设置Embedded Content Contains Swift Code
为NO
.打开
<YOUR_PROJECT_NAME>-Prefix.pch
, 移除#import <YOUR_PROJECT_NAME>-Swift.h
. 比方:#ifdef __OBJC__ #import "TestProject-Swift.h" #endif
又一次build, 检查是否正常.
Cordova 项目高速向导
此向导展示了在开发中怎样高速使用这个插件. 我们须要加入 开发扩展 。须要 Xcode 7, 虽然hot code push plugin插件自身能够支持低版本号xcode.
创建新的Cordova项目。并加入android和iOS platform:
cordova create TestProject com.example.testproject TestProjectcd ./TestProject cordova platform add android cordova platform add ios
或者能够用一个已有的项目.
加入插件:
cordova plugin add cordova-hot-code-push-plugin
加入开发扩展:
cordova plugin add cordova-hot-code-push-local-dev-addon
安装 Cordova Hot Code Push 命令行client:
npm install -g cordova-hot-code-push-cli
启动本地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
打开新的控制台, 进入到项目根文件夹,执行app:
cordova run
稍等,app会安装到手机或者模拟器.
如今打开 TestProject/www/
index.html
, 做一些修改然后保存. 几秒种后你能够在手机或模拟器上看到更新后的页面.
到此,你能够本地开发。新的web内容会自己主动在设备上更新,而无需又一次启动app查看效果.
Ionic 项目高速向导
此向导展示了在开发中怎样高速使用这个插件. 我们须要加入 开发扩展 。须要 Xcode 7, 虽然hot code push plugin插件自身能够支持低版本号xcode.
创建新的Ionic项目,并加入android和iOS platform::
ionic start TestProject blankcd ./TestProject ionic platform add android ionic platform add ios
Or use the existing one.
加入插件:
ionic plugin add cordova-hot-code-push-plugin
加入开发扩展:
ionic plugin add cordova-hot-code-push-local-dev-addon
安装 Cordova Hot Code Push 命令行client:
npm install -g cordova-hot-code-push-cli
启动本地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
打开新的控制台, 进入到项目根文件夹。执行app:
ionic run
稍等。app会安装到手机或者模拟器.
如今打开 TestProject/www/
index.html
, 做一些修改然后保存. 几秒种后你能够在手机或模拟器上看到更新后的页面.
到此,你能够本地开发,新的web内容会自己主动在设备上更新。而无需又一次启动app查看效果.
更新机制的流程图
先防止全部的配置相关的内容弄得你稀里糊涂 - 先来看看此插件的实现更新功能的流程图. 应该没有技术细节.
用户打开你的app.
插件初始化,在后台进程启动 升级载入器(update loader).
Update loader 从
config.xml
取config-file
配置(一个url),并从此url载入一段 JSON 配置. 然后它把这段JSON配置中的release
版本 和当前app 已经安装的进行比較. 假设不同 - 进入下一步.Update loader 使用app配置(application config)中的
content_url
。去载入清单文件(manifest). 它会找出自上次升级以来,哪些文件须要更新.Update loader 从
content_url
下载更新文件.假设一切顺利 - 发出一个"升级文件已经准备好,能够安装了"的通知.
升级文件已安装, 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文件, 发生情况例如以下:
在外部存储创建了一个以新的 release 版本为名字的文件夹:
/sdcard/some_path/2016.01.03-10.45.01/
.文件夹里面 - 又创建了一个
update
文件夹 :/sdcard/some_path/2016.01.03-10.45.01/update/
.全部依据
chcp.manifest
更新的文件 都被下载到了这个update
文件夹内.新的
chcp.manifest
和chcp.json
也被放到了update
文件夹内.新的web内容已准备安装.
安装更新的时候:
插件从当前正在使用的release版本号 文件夹内拷贝
www
下全部内容到 新的 release 版本号文件夹下. 用我们的样例就是:从/sdcard/some_path/2015.12.01-12.01.33/www/
拷贝全部文件到/sdcard/some_path/2016.01.03-10.45.01/www/
.从
update
文件夹下拷贝新的web内容和配置文件,到www
文件夹下:/sdcard/some_path/2016.01.03-10.45.01/update/
->/sdcard/some_path/2016.01.03-10.45.01/www/
.移除
/sdcard/some_path/2016.01.03-10.45.01/update/
文件夹。由于我们不再使用了.载入新的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时 - 一般做法类似:
web项目做一些更改.
运行
cordova run
启动app.稍等一会查看执行结果.
即使非常小的变更也须要打包重装app. 耗时比較久,比較麻烦.
为了提升速度 - 你能够使用本地开发扩展 Hot Code Push Local Development Add-on. 安装非常简答:
加入此cordova插件.
启动本地服务
cordova-hcp server
.在你的项目的
config.xml
文件里<chcp />
块下加入<local-development enabled="true" />
.启动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, 按下它能够检查更新. 我们须要这样写代码:
监听button的
click
事件.当点击button时调用
chcp.fetchUpdate()
.处理更新事件的结果.
我们来改 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更新. 此时, 我们应该这样:
公布新版本号的web内容, 它们能够用于最初的
config-file
url.在新的版本号
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).
这里你想怎么做就怎么做. 经常用法是显示一个对话框,问用户是否须要转到应用商店. 插件也提供了这个.
你须要做的是:
在 application config(chcp.json) 中设置t
android_identifier
和ios_identifier
.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
.
关于热更新的流程解析
好多同学都測试不成功。大家不要想太复杂了。我再简要概括一下:
chcp.json文件里的content_url为server项目的地址加port号
config.xml为server项目地址加port号再加上/chcp.json
每次改动完文件后。必须将【改动的文件】和【chcp.manifest文件】一并拷贝到server项目中进行覆盖。
将server中的chcp.json文件里的【"release": "2016.08.04-18.04.06"】时间改为当前时间。
3 和 4是最重要的。不然热更新就不起作用。
最后你们不要在纠结cordova-hcp server,这个东西就是在开发的时候启动用来监听文件的改动。假设有文件改动。就相应在chcp.manifest中改动该文件的hash值。
还没完。为了更清楚的了解热更新是怎么回事,这里我画了一张图。
[热更新的流程解析]
app启动
从server请求chcp.json文件(会覆盖本地chcp.json文件)。
server返回chcp.json文件与app里的chcp.json文件做对照,推断两个文件里的release时间。
假设serverchcp.json文件的release时间大于app里chcp.json的release时间(说明新的资源)
假设有新的资源。再次发送一个请求,请求server的chcp.manifest文件(会覆盖本地chcp.json文件)。
server返回chcp.manifest文件与app里的chcp.manifest文件内容做对照。
假设有不一样的hash值。
对server请求新的资源。
请求成功的资源将覆盖本地资源。
案例
这里通过对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 server 和public 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群: idkey=d578312312c7dd1a7cbf0f7b7080a40feb21843740c29e2e5588f1987e95267a" style="color:rgb(202,0,0); text-decoration:none">194182999 (满)
2群:419834979
共同学习交流(博主QQ: v=3&uin=479858761&site=qq&menu=yes" style="color:rgb(202,0,0); text-decoration:none">479858761)
[Phonegap+Sencha Touch] 移动开发77 Cordova Hot Code Push插件实现自己主动更新App的Web内容