[圖片上傳失敗...(image-a19be7-1521624289049)]javascript
基於 Cordova 框架能將網頁應用 (js, html, css, 圖片等) 打包成 App。當 App 在應用商店上架後,如何快速更新是咱們須要考慮的問題。🤖css
咱們能想的這兩種方式都存在的很大的弊端,不適合實際應用!html
插件 Cordova Hot Code Push 正是針對 Cordava 應用如何快速更新問題而提供的解決方案,能夠自動更新 web 相關的靜態文件。java
該插件提供了詳細的 wiki 文檔,請參考:wiki 文檔android
近日,蘋果App Store審覈團隊向一些開發者下最後通牒:2017年6月12日以前移除全部熱更新相關代碼、框架或SDK,並從新提交版本。若是不做調整,App可能會從App Store下架。ios
蘋果應用商店已經禁止使用相似 JSPatch 等熱修復的框架或者SDK,那麼這個插件提供的代碼熱更新功能是否違法這一規定呢? 🤔
📌 答案是否認的!此插件提供的代碼熱更新是 web 靜態文件,蘋果是容許這一作法的。但有兩點值得注意:git
一、下載插件
要求 Cordova 版本 5.0+github
cordova plugin add cordova-hot-code-push-plugin --save
二、下載插件的命令行工具 cordova-hot-code-push-cliweb
npm install -g cordova-hot-code-push-cli
該命令行工具可幫助咱們自動生成配置文件 chcp.json
和 chcp.manifest
,同時還提供了一些其餘功能,詳細可參考其 README。npm
三、下載插件後,執行 cordova platform add ios
時可能會遇到以下報錯:
Error: Cannot find module 'xml2js/lib/processors'
參考 xml2js is not installed 解決方法很簡單:npm install xml2js
Cordova Hot Code Push 下載安裝到項目中後,須要對其進行相關的配置才能讓其工做。
Cordova Hot Code Push 熱更新插件須要兩個配置文件:
chcp.json
包含發佈相關信息:熱更新代碼版本號,應用 native side 版本號等等chcp.manifest
包含項目熱更新代碼(靜態)文件信息:文件名和文件哈希值這兩個配置文件對於插件的運行缺一不可,前者描述了熱更新代碼的版本信息,後者提供了熱更新代碼文件的變動信息。藉助 cordova-hot-code-push-cli
這個命令行工具能夠輔助咱們建立這兩個配置文件。
Application config holds information about the current release of the web project.
chcp.json
置於 www
目錄根目錄,例子以下:
{
"name": "wps-*****",
"content_url": "https://kss.ksyun.com/*****/*****/",
"ios_identifier": "326CN*****",
"android_identifier": "com.**********.*****.*****.*****.*****",
"update": "resume",
"release": "2017.06.07-16.30.20",
"min_native_interface": 1
}
一、配置項
① name
項目名稱
② content_url
web 項目文件在服務器上的存儲路徑(即 www 目錄在雲存儲中的目錄路徑),熱更新插件將此 URL 做爲 base url,用於下載配置文件和項目更新文件(必需的配置項)
③ release
描述 web 項目版本號,每一次發佈的版本號必須惟一(默認使用時間戳,格式爲:yyyy.MM.dd-HH.mm.ss),插件是將版本號進行字符串相等比較來判斷是否存在新版本(必需的配置項)
④ min_native_interface
Minimum version of the native side that is required to run this web content
min_native_interface
的值在應用 config.xml
配置中能夠定義了 native side 的版本號,例如
<chcp>
<native-interface version="5" /> </chcp>
例如當前項目 native side 的版本號是5:
chcp.json
中的 min_native_interface
值爲 5,那麼符合要求,熱更新後的 web content 可以在正常運行chcp.json
中的 min_native_interface
值爲 10,高於 config.xml
文件中 <native-interface />
,那麼熱更新將沒法正常進行。此時,插件會提示錯誤 chcp_updateLoadFailed
,提示應用須要更新升級⑤ update
什麼時候觸發進行安裝(install)代碼熱更新
代碼熱更新涉及兩個主要過程:fetch update 和 install update。前者獲取熱更新變動文件,後者將獲取到的更新文件安裝到 App 中生效。此字段是針對後者,什麼時候 install update,可選值:
start
:應用啓動,默認項(install update when application is launched)resume
:應用從後臺恢復(install the update when application is resumed from background state)now
:下載更新後當即執行(install update as soon as it has been downloaded)固然也能夠禁用自動 install update,手動調用相關 API 進行 install
⑥ android_identifier
/ ios_identifier
android_identifier
: Package name of the Android version of the applicationios_identifier
: Identification number of the application二、如何生成該文件:
cordova-hcp init
,會經過命令行交互的方式,提示輸入配置有關信息,建立該文件,會在項目根目錄建立一個默認 Application config 文件 cordova-hcp.json
cordova-hcp build
便可在 web 項目 www
根目錄生成一個 chcp.json
文件。Content manifest describes the state of the files inside your web project.
經過執行 cordova-hcp build
在 www
根目錄自動生成 chcp.manifest
文件
[
{
"file": "import.html",
"hash": "fc9301d4bd7381ba6033aa51884ed2dd"
},
{
"file": "index.html",
"hash": "f73630f62a531ab6c41cd067eb4f9b07"
},
{
"file": "lib/lib.min.js",
"hash": "6ecb0251f4c54f80586d9059dfc61de8"
},
...
]
chcp.manifest
文件中包含的是 web content 靜態文件信息,每個項都包括兩個字段:
file
: 相對於 www
目錄的文件路徑hash
: 文件的 MD5 哈希值,用於判斷文件是否發生變動基於 chcp.manifest
文件
config.xml
配置Cordova 項目的 config.xml
文件用於設置項目配置選項,Cordova Hot Code Push 熱更新插件的配置項也須要在該文件中進行相應的配置。
<chcp>
<config-file url="https://kss.ksyun.com/********/chcp.json" />
<auto-download enabled="false" />
<auto-install enabled="false" />
<native-interface version="1" />
</chcp>
</pre>
config-file
:配置文件 chcp.json
從服務器上加載的路徑(必須的配置項)auto-download
:是否自動下載熱更新代碼,默認是 trueauto-install
:是否自動安裝熱更新代碼,默認是 truenative-interface
:當前 native side 的版本號能夠禁用自動下載,安裝熱更新代碼,經過手動調用執行。
[圖片上傳失敗...(image-e57e54-1521624148035)]
config.xml
文件中獲取 config-file
(熱更新插件配置文件 chcp.json
的加載路徑),而後加載配置文件 chcp.json
,獲取其中的 release
版本號,對比當前的版本號,若兩者不一樣,說明有新版本,執行下一步chcp.json
配置文件中獲取 content_url
做爲 base url,而後加載 chcp.manifest
文件,或者新版本文件變動信息content_url
做爲 base url,下載全部變動、新增文件Cordova 項目中都包含一個 www
目錄,存儲網頁靜態文件,Cordova 打包移動應用時,會將其拷貝到各自的項目目錄,同時會被打包到應用中。
www
目錄打包到應用中以後,咱們就沒辦法對其進行更新了,由於只有可讀權限。爲了解決這一問題,在應用第一次啓動的時候,從應用 bundle 中加載網頁內容的同時,將 www
目錄拷貝到外部目錄中,在後續應用啓動時,都從這個外部存儲的靜態文件中加載文件,而對於外部的這個存儲目錄,咱們就有讀寫權限,這樣就爲咱們動態更新網頁代碼提供了可能。
在 safari 調試頁面執行 cordova.file.applicationStorageDirectory
能夠獲得應用的存儲路徑,點擊能夠打開 Finder 目錄。
從 Library/Application Support
目錄下就能夠找到存儲 web content 的外部目錄。
[圖片上傳失敗...(image-d0c218-1521624148035)]
[圖片上傳失敗...(image-5ff6fd-1521624148035)]
Cordova Hot Code Push 插件爲每個版本內容都建立了一個對應的目錄,以配置文件 chcp.json
中 release
字段值爲目錄名,存放不一樣版本 www
目錄中的靜態文件,這種處理方式的好處是:
🤖 下面瞭解一下,獲取更新內容和安裝更新內容時都發生了什麼?
一、獲取更新內容
release
版本號,建立一個新的目錄update
目錄,根據 chcp.manifest
文件,將全部變動、新增文件下載到該目錄中chcp.json
和 chcp.manifest
文件也會置於 update
目錄中[圖片上傳失敗...(image-b49207-1521624148035)]
二、安裝更新內容
www
目錄拷貝到新版本對應的目錄下update
目錄中變動、新增文件拷貝到 www
目錄中,同時根據 chcp.manifest
移除被刪除文件update
目錄默認狀況下,Cordova Hot Code Push (CHCP) 插件不須要額外的代碼,就能夠自動執行 checking->downloading->installation
這個更新循環。固然也能夠經過其提供的接口來控制這更新流程,這時,咱們須要在項目 config.xml
文件中配置 auto-download
和 auto-install
爲 false
<chcp>
<config-file url="https://kss.ksyun.com/******/chcp.json" />
<auto-download enabled="false" />
<auto-install enabled="false" />
<native-interface version="1" />
</chcp>
Cordova Hot Code Push 插件提供了一系列事件監聽,方便咱們對不一樣狀況進行不一樣的處理。例如:chcp_updateInstalled
事件,當更新安裝完成時會發出這個通知;chcp_updateInstallFailed
事件,當更新安裝失敗時發出這個通知,等等。
值得注意的是,須要在 deviceready
事件回調後,才進行 CHCP 插件的事件監聽註冊。
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();
詳細事件監聽列表參考文檔:Listen for update events
① fetch update chcp.fetchUpdate
調用 API 從服務器中獲取更新
function fetchUpdate(cb) {
var options = {
'config-file': 'https://kss.ksyun.com/******/chcp.json'
};
chcp.fetchUpdate(updateCallback, options);
function updateCallback(error, data) {
if (error) {
console.log('--fetchUpdate error--', error.code, error.description);
}
console.log('--fetchUpdate--', data, data.config);
cb && cb(error, data);
}
}
② install update chcp.installUpdate
調用 API 安裝更新
function installUpdate(cb) {
chcp.installUpdate(installationCallback);
function installationCallback(error) {
if (error) {
console.log('Failed to install the update with error code: ' + error.code);
console.log(error.description);
} else {
console.log('Update installed!');
}
cb && cb(error);
}
}
在安裝更新以前,還須要檢測是否有更新可用於安裝
chcp.isUpdateAvailableForInstallation
function checkIsUpdateAvailableForInstallation(cb) {
chcp.isUpdateAvailableForInstallation(callbackMethod);
function callbackMethod(error, data) {
if (error) {
console.log('No update was loaded => nothing to install');
} else {
console.log('Current content version: ' + data.currentVersion);
console.log('Ready to be installed:' + data.readyToInstallVersion);
}
cb && cb(error, data);
}
}
function getVersionInfo(cb) {
chcp.getVersionInfo((err, data) => {
console.log('Current web version: ' + data.currentWebVersion);
console.log('Previous web version: ' + data.previousWebVersion);
console.log('Loaded and ready for installation web version: ' + data.readyToInstallWebVersion);
console.log('Application version name: ' + data.appVersion);
console.log('Application build version: ' + data.buildVersion);
cb && cb(err, data);
});
}
在下載,安裝更新過程當中都有可能出現錯誤,詳細的錯誤代碼參考:Error codes
插件配置文件 chcp.json
中 min_native_interface
選項是網頁內容執行時要求 native side 最低版本號。每一次熱更新過程當中,都會去檢查這個邏輯,判斷當前 native side 的版本是否符合要求。若是當前 APP 中的 native side 版本號低於 chcp.json
中 min_native_interface
的選項值,那麼執行熱更新就會提示錯誤:chcp.error.APPLICATION_BUILD_VERSION_TOO_LOW
,這個時候,咱們應當提示用戶前往應用商店對 APP 進行升級。
恰當的處理方式是,在出現 chcp.error.APPLICATION_BUILD_VERSION_TOO_LOW
錯誤時,彈框提示用戶前往應用商店進行升級,彈框有兩個按鍵:一個點擊後跳轉到應用商店該 APP 對應下載頁面;另外一個點擊後關閉彈框。插件也提供了 API 處理這個過程,咱們只需:
chcp.json
配置文件中設置 android_identifier
和 ios_identifier
chcp.requestApplicationUpdate
方法監聽 chcp_updateLoadFailed
事件,判斷錯誤代碼爲 chcp.error.APPLICATION_BUILD_VERSION_TOO_LOW
時,調用 chcp.requestApplicationUpdate
方法。
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();
一、Don’t rename/delete/move your index page
Cordova 項目 config.xml
文件中都會定義一個入口頁面 index.html
:
<content src="index.html" />
應用啓動的時候,就會加載 index.html
頁面做爲入口,在代碼熱更新過程當中,這是惟一不能刪除,移動和重命名的文件,不然,代碼熱更新後,應用就沒法正常加載到 index.html
入口頁面,因此會出錯。
誠然,若是你須要重命名,或者修改其存儲路徑,那麼須要在 config.xml
文件中修改 content
配置。
二、Do not clean plugin’s inner preferences with cordova-plugin-nativestorage
cordova-plugin-nativestorage 插件提供了讀寫本地存儲數據的能力,例如在 iOS 中對應的本地存儲是 NSUserDefault
,CHCP 熱更新插件在其中存儲了一些屬性。
調用 cordova-plugin-nativestorage 插件中的 NativeStorage.clear()
方法會清除本地存儲數據,這就會影響到 CHCP 插件的正常運行,致使下一次應用啓動時加載的是應用 bundle 中 www
目錄中的網頁內容,而非外部目錄存儲的當前版本網頁內容。
www
目錄打包上傳到服務器或者雲存儲目錄新版本發佈時,都須要執行以下處理:
www
目錄下的靜態文件進行打包,包括代碼壓縮,合併等等cordova-hcp build
生成 chcp.json
和 chcp.manifest
文件www
目錄下的靜態文件上傳至服務器或者雲存儲目錄問題總結:
1 ERROR: Plugin 'HotCodePush' not found
The problem is resolved. Looking at forums, below are the correct steps
1 Run cordova plugin add cordova-hot-code-push-plugin
in the project
2 Install npm install -g cordova-hot-code-push-cli
on the system
3 Run cordova-hcp init
and enter details
4 Run cordova-hcp build
5 Add the following in config.xml
<chcp>
<config-file url="https://example.com/chcp.json"/> </chcp>
Run cordova build
2 建立模版cordova-hcp.json,放在項目根目錄下,避免每次修改chcp.json文件
{
"name": "wps-*****",
"content_url": "https://kss.ksyun.com/*****/*****/",
"ios_identifier": "326CN*****",
"android_identifier": "com.**********.*****.*****.*****.*****",
"update": "resume",
"release": "2017.06.07-16.30.20",
"min_native_interface": 1
}
以後每次修改代碼後,在項目根目錄運行 cordova-hcp build
就能夠在www目錄生成跟模版同樣的chcp.json,只是release
時間不同
3 補充,手動更新
以上教程的熱更新屬於自動化的,在用戶徹底不知情的狀況下,因而就有了可選擇更新的需求:
<chcp>
<auto-download enabled="false" />
<auto-install enabled="false" />
<config-file url="http://120.24.77.175:8080/ehospital/views/MSUI/chcp.json" />
</chcp>
在config.xml改爲以上配置,取消自動下載,取消自動安裝,而後經過插件提供的jsAPI進行手動更新,如下在deviceready事件觸發中示例(僅供參考):
document.addEventListener('deviceready', () => {
let chcp = window.chcp;
// 檢測更新
chcp.fetchUpdate((error, data) => {
// 表示沒有更新版本,或者其餘錯誤,詳情的信息參考上面的chcp error連接
if (error) {
console.log('--fetchUpdate error--', error.code, error.description);
return;
}
// 此次更新的版本信息
console.log('--fetchUpdate--', data, data.config);
// 檢測是不是否能夠進行安裝了,雙重判斷吧,有時候會出現有更新版本可是暫時沒法安裝的狀況(也能夠去掉這一層)
chcp.isUpdateAvailableForInstallation((error, data) => {
if (error) {
console.log('No update was loaded => nothing to install');
} else {
// 詢問用戶是否更新
if ( window.confirm('檢測到新版本,是否更新') ) {
// 更新中
chcp.installUpdate((error) => {
if (error) {
// 更新失敗
console.log('Failed to install the update with error code: ' + error.code);
console.log(error.description);
} else {
// 更新成功
console.log('Update installed!');
}
});
} else {
window.alert('您已拒絕更新');
}
// 對比版本號
console.log('Current content version: ' + data.currentVersion);
console.log('Ready to be installed:' + data.readyToInstallVersion);
}
});
});
});