Cordova Hot Code Push插件實(shí)現(xiàn)自動(dòng)更新App的Web內(nèi)容

https://blog.csdn.net/wskai1/article/details/59055534

稍等犬金,app會(huì)安裝到手機(jī)或者模擬器.
  1. 現(xiàn)在打開 TestProject/www/index.html , 做一些改動(dòng)然后保存. 幾秒種后你可以在手機(jī)或模擬器上看到更新后的頁(yè)面.

到此,你可以本地開發(fā)垄懂,新的web內(nèi)容會(huì)自動(dòng)在設(shè)備上更新蛛倦,而無(wú)需重新啟動(dòng)app查看效果.

更新機(jī)制的流程圖

先防止所有的配置相關(guān)的內(nèi)容弄得你稀里糊涂 - 先來(lái)看看此插件的實(shí)現(xiàn)更新功能的流程圖. 應(yīng)該沒有技術(shù)細(xì)節(jié).

image
  1. 用戶打開你的app.

  2. 插件初始化浑玛,在后臺(tái)進(jìn)程啟動(dòng) 升級(jí)加載器(update loader).

  3. Update loader 從 config.xmlconfig-file 配置(一個(gè)url)憎亚,并從此url加載一段 JSON 配置. 然后它把這段JSON配置中的 release 版本號(hào) 和當(dāng)前app 已經(jīng)安裝的進(jìn)行比較. 如果不同 - 進(jìn)入下一步.

  4. Update loader 使用app配置(application config)中的 content_url 衙耕,去加載清單文件(manifest). 它會(huì)找出自上次升級(jí)以來(lái)刃泌,哪些文件需要更新.

  5. Update loader 從 content_url下載更新文件.

  6. 如果一切順利 - 發(fā)出一個(gè)"升級(jí)文件已經(jīng)準(zhǔn)備好凡壤,可以安裝了"的通知.

  7. 升級(jí)文件已安裝, app重新進(jìn)入更新過(guò)的頁(yè)面.

當(dāng)然, 還有其他的細(xì)節(jié), 不過(guò)你已經(jīng)有了大致的思路.

web內(nèi)容是如何存儲(chǔ)和更新的

每一個(gè)Cordova 項(xiàng)目都有一個(gè) www 目錄, 這里存放所有的web內(nèi)容. 當(dāng)cordova build 執(zhí)行后 - www 里的內(nèi)容會(huì)拷貝到對(duì)應(yīng)platform的 www 目錄下:

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

  • iOS: platforms/ios/www.

于是這些文件被打包進(jìn)了app. 我們不能更新安裝包里的這些文件, 因?yàn)樗鼈兪侵蛔x的. 正因?yàn)槿绱耍晕覀円赼pp第一次啟動(dòng)的時(shí)候耙替,將內(nèi)置的web內(nèi)容(www目錄)復(fù)制到外部存儲(chǔ). 我們不想在拷貝過(guò)程中阻塞ui - 我們還是會(huì)先加載app內(nèi)置的index.html. 但是下一次啟動(dòng)或更新 - 我們就從外部存儲(chǔ)加載index.html.

但是如果你的app外殼需要增加新的cordova插件或者原生功能 - 你必須要重新上架外殼app到store商店. 還有 - 增加外殼 app 的build版本號(hào) (App Store 或 Google Play強(qiáng)制的).下次啟動(dòng)亚侠,插件檢查外殼app版本號(hào)是否變化, 如果變了 - 會(huì)重新拷貝內(nèi)置web內(nèi)容(www目錄)到外部存儲(chǔ).

開發(fā)app的時(shí)候 - 你可能會(huì)困惑: 改了一些文件, 重新啟動(dòng)了app - 但卻看到的是舊的頁(yè)面. 現(xiàn)在你知道原因了: 插件用的是舊版本的web內(nèi)容(外部存儲(chǔ)中). 若要清除緩存,你需要:

  • 卸載app, 執(zhí)行 cordova run.

  • 增加外殼app版本號(hào)俗扇,強(qiáng)制插件重新安裝 www 目錄. 更改外殼app版本號(hào)請(qǐng)?jiān)O(shè)置 config.xml文件的 android-versionCodeios-CFBundleVersion .

  • 安裝 本地開發(fā)擴(kuò)展 硝烂,讓它幫你處理版本號(hào)問(wèn)題. 每次build他會(huì)自動(dòng)幫你app的build版本號(hào)加1,不需要你手動(dòng)更改

上面就是簡(jiǎn)要介紹, 以便你理解大致的思路. 現(xiàn)在我們繼續(xù)深入.

之后你會(huì)閱讀到 配置文件 這一節(jié)- 這有app配置 (application config), 名字是chcp.json. 里面有個(gè) release設(shè)置, 這個(gè)指明了web內(nèi)容的版本. 這個(gè)配置必須而且每次發(fā)布的release版本必須不一樣. 它由 命令行客戶端 自動(dòng)生成狐援,格式是: yyyy.MM.dd-HH.mm.ss (比如 2015.09.01-13.30.35).

每次發(fā)布钢坦,插件在外部存儲(chǔ)自動(dòng)生成一個(gè)以這個(gè) release版本 為名字的目錄, 然后把web內(nèi)容全部放到這里面. release版本號(hào)成為了 url的一部分. 這個(gè)手段可以解決一些問(wèn)題:

  • 網(wǎng)頁(yè)內(nèi)容緩存問(wèn)題. 比如, iOS 上,css 文件會(huì)被 UIWebView緩存起來(lái), 即使我們重新載入了index.html - 新的樣式還是不會(huì)被應(yīng)用. 你需要用任務(wù)管理器殺死app, 或者改變css的路徑.

  • 基本不會(huì)發(fā)生更新后損壞已有web內(nèi)容的現(xiàn)象, 因?yàn)槲覀兠看胃露荚诓煌哪夸浵?

  • 即使更新導(dǎo)致了web內(nèi)容損壞 - 我們可以回滾到上一個(gè)版本的release.

比如, 我們當(dāng)前運(yùn)行的release版本是 2015.12.01-12.01.33. 這意味著:

  • 所有web內(nèi)容存儲(chǔ)在 /sdcard/some_path/2015.12.01-12.01.33/www/. 包含了Cordova的資源.

  • Index 頁(yè)面, 用戶看到的是 /sdcard/some_path/2015.12.01-12.01.33/www/index.html.

某個(gè)時(shí)候我們發(fā)布了一個(gè)新的release: 2016.01.03-10.45.01. 第一步啥酱,插件需要下載新的web文件, 發(fā)生情況如下:

  1. 在外部存儲(chǔ)創(chuàng)建了一個(gè)以新的 release 版本號(hào)為名字的目錄: /sdcard/some_path/2016.01.03-10.45.01/.

  2. 目錄里面 - 又創(chuàng)建了一個(gè) update 目錄 : /sdcard/some_path/2016.01.03-10.45.01/update/.

  3. 所有根據(jù) chcp.manifest 更新的文件 都被下載到了這個(gè) update 目錄內(nèi).

  4. 新的 chcp.manifestchcp.json 也被放到了 update 目錄內(nèi).

  5. 新的web內(nèi)容已準(zhǔn)備安裝.

安裝更新的時(shí)候:

  1. 插件從當(dāng)前正在使用的release版本 目錄內(nèi)拷貝 www 下所有內(nèi)容到 新的 release 版本目錄下. 用我們的例子就是:從 /sdcard/some_path/2015.12.01-12.01.33/www/ 拷貝所有文件到 /sdcard/some_path/2016.01.03-10.45.01/www/.

  2. update 目錄下拷貝新的web內(nèi)容和配置文件爹凹,到 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/ 目錄,因?yàn)槲覀儾辉偈褂昧?

  4. 加載新的release版本index.html: /sdcard/some_path/2016.01.03-10.45.01/www/index.html.

至此镶殷,插件會(huì)從新的release加載頁(yè)面, 而舊的release則會(huì)作為一個(gè)備份留下來(lái)禾酱,以防萬(wàn)一.

Cordova Hot Code Push 命令行客戶端

Cordova Hot Code Push 命令行客戶端 是一個(gè)命令行工具,以便你web內(nèi)容的開發(fā).

它可以:

  • 生成 chcp.jsonchcp.manifest 文件, 這樣你就不用手動(dòng)去創(chuàng)建;

  • 運(yùn)行本地服務(wù)绘趋,開發(fā)時(shí)可以檢測(cè)更新颤陶,并發(fā)布新的release版本,使得可以再設(shè)備上實(shí)時(shí)更新web內(nèi)容;

  • 部署你的web內(nèi)容到外部服務(wù)器上.

當(dāng)然, 你可以不使用這個(gè)命令行工具. 只是用了它會(huì)更方便一些.

本地開發(fā)擴(kuò)展

當(dāng)你本地開發(fā)app時(shí) - 一般做法類似:

  1. web項(xiàng)目做一些更改.

  2. 執(zhí)行 cordova run 啟動(dòng)app.

  3. 稍等一會(huì)查看運(yùn)行結(jié)果.

即使很小的變更也需要打包重裝app. 耗時(shí)比較久陷遮,比較麻煩.

為了提升速度 - 你可以使用本地開發(fā)擴(kuò)展 Hot Code Push Local Development Add-on. 安裝很簡(jiǎn)答:

  1. 添加此cordova插件.

  2. 啟動(dòng)本地服務(wù) cordova-hcp server.

  3. 在你的項(xiàng)目的config.xml 文件中 <chcp /> 塊下添加 <local-development enabled="true" />.

  4. 啟動(dòng)app.

這樣, 所有web內(nèi)容的變更都會(huì)被插件檢測(cè)到, 并直接更新顯示到app上滓走,而不需要重啟app.

只有在添加了新的cordova插件時(shí)你才會(huì)重啟app.

重要: 你應(yīng)該只在開發(fā)狀態(tài)下使用此擴(kuò)展. 發(fā)布外殼app的時(shí)候,應(yīng)該移除此擴(kuò)展: cordova plugin remove cordova-hot-code-push-local-dev-addon.

Cordova 配置項(xiàng)

你應(yīng)該知道, Cordova 使用 config.xml 文件配置不同項(xiàng)目: app名字, 描述, 起始頁(yè)面帽馋,等等. 使用config.xml文件搅方,你也可以為此插件配置.

這些配置位于 <chcp> 塊. 比如:

<pre style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 16px; position: relative; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; white-space: pre-wrap; overflow-wrap: normal; overflow: auto; font-size: 13.6px; line-height: 1.45; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; word-break: normal; background-color: rgb(247, 247, 247);"><chcp>
<config-file url="https://5027caf9.ngrok.com/chcp.json"/>
</chcp></pre>

config-file

定義了一個(gè) URL比吭,指定了需要從哪里加載app配置(application config,就是chcp.json). URL 在 url 屬性中聲明. 此項(xiàng)必須.

以防萬(wàn)一姨涡,開發(fā)的時(shí)候, 如果 config-file 沒有定義 - 會(huì)自動(dòng)設(shè)為本地服務(wù)上 chcp.json 的路徑.

auto-download

自動(dòng)下載web內(nèi)容更新. 默認(rèn)是自動(dòng), 如果你想手動(dòng)下載web內(nèi)容更新衩藤,你可以使用 JavaScript 模塊(下面有).

禁用自動(dòng)下載可以設(shè)置 config.xml:

<pre style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 16px; position: relative; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; white-space: pre-wrap; overflow-wrap: normal; overflow: auto; font-size: 13.6px; line-height: 1.45; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; word-break: normal; background-color: rgb(247, 247, 247);"><chcp>
<auto-download enabled="false" />
</chcp></pre>

默認(rèn)是 true.

auto-install

自動(dòng)安裝. web內(nèi)容更新. 默認(rèn)是自動(dòng), 如果你想手動(dòng)安裝web內(nèi)容更新,你可以使用 JavaScript 模塊(下面有).

禁用自動(dòng)安裝可以設(shè)置 config.xml:

<pre style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 16px; position: relative; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; white-space: pre-wrap; overflow-wrap: normal; overflow: auto; font-size: 13.6px; line-height: 1.45; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; word-break: normal; background-color: rgb(247, 247, 247);"><chcp>
<auto-install enabled="false" />
</chcp></pre>

默認(rèn)是 true.

配置文件

此插件用到2個(gè)配置文件:

  • app配置 Application config - 包含最新的release信息: release 版本號(hào), 最低需要的外殼app版本號(hào)涛漂,等等. 文件名 chcp.json

  • Web內(nèi)容清單 Content manifest - 包含所有web內(nèi)容文件的名字和MD5值. 文件名 chcp.manifest

這兩個(gè)文件必須. 他們描述了是否有新的release版本赏表,以及文件更新時(shí)的比較.

還有一個(gè)build 可選參數(shù) (build options) 文件, 可以再執(zhí)行cordova build 命令時(shí)指定插件的配置.

Application config app配置

包含最新版本的release信息.

簡(jiǎn)單的例子:

<pre style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 16px; position: relative; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; white-space: pre-wrap; overflow-wrap: normal; overflow: auto; font-size: 13.6px; line-height: 1.45; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; word-break: normal; background-color: rgb(247, 247, 247);">{ "content_url": "https://5027caf9.ngrok.com", "release": "2015.09.01-13.30.35"}</pre>

這個(gè)文件應(yīng)該放在 www 目錄下匈仗,文件名是 chcp.json . 這個(gè)文件也被打包到了外殼app內(nèi).

你可以手動(dòng)創(chuàng)建它, 或者用 cordova-hcp 命令(Cordova Hot Code Push 命令行)自動(dòng)生成. 只要在cordova項(xiàng)目根目錄下運(yùn)行 cordova-hcp init , 以后要發(fā)布新的release只要執(zhí)行 cordova-hcp build. 更多內(nèi)容請(qǐng)閱讀 命令行客戶端的文檔.

content_url

服務(wù)端URL, 也就是你所有web內(nèi)容文件的位置. 插件會(huì)把它作為下載新的清單文件瓢剿、新的web內(nèi)容文件的 base url. 此項(xiàng)必須.

release

任何字符串. 每次release應(yīng)該唯一. 插件基于這個(gè)才知道有沒有新版本release. 此項(xiàng)必須.

重要: 插件只比較release字符串是否相等, 如果不等,就認(rèn)為服務(wù)端有新版本.(不會(huì)比較大忻小)

min_native_interface

所需最小的外殼app版本. 這是app的build版本號(hào)跋选,是個(gè)整型數(shù)字, 不是應(yīng)用商店中看到的形如"1.0.0"字符串.

config.xml中,這樣指定build版本號(hào):

<pre style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 16px; position: relative; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; white-space: pre-wrap; overflow-wrap: normal; overflow: auto; font-size: 13.6px; line-height: 1.45; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; word-break: normal; background-color: rgb(247, 247, 247);"><widget id="io.cordova.hellocordova"
version="1.0.1"
android-versionCode="7"
ios-CFBundleVersion="3"></pre>

  • version - app字符串版本號(hào), 也就是用戶在商店中看到的版本.

  • android-versionCode - 安卓的build版本號(hào). 這個(gè)應(yīng)該用于 min_native_interface.

  • ios-CFBundleVersion - iOS的build版本號(hào).這個(gè)應(yīng)該用于 min_native_interface.

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

重要: 因?yàn)?a target="_blank" rel="nofollow">cordova的一個(gè)奇葩現(xiàn)象, 生成的 .apk 的build版本號(hào)會(huì)被加 10, 導(dǎo)致了變成了形如 70, 72, or 74, 根據(jù)不同平臺(tái) (arm/x86/etc)哗蜈,后面的0前标、2、4不一樣. 為了繞過(guò)這個(gè), 我們建議也給 iOS build版本號(hào)手動(dòng)加10, 這樣 min_native_interface (比如 70) 就可以對(duì)安卓和iOS都有效, 大致是這樣:

<pre style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 16px; position: relative; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; white-space: pre-wrap; overflow-wrap: normal; overflow: auto; font-size: 13.6px; line-height: 1.45; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; word-break: normal; background-color: rgb(247, 247, 247);"><widget id="io.cordova.hellocordova"
version="1.0.1"
android-versionCode="7"
ios-CFBundleVersion="70"></pre>

舉個(gè)例子, 假設(shè)你的外殼app加了個(gè)新的插件 - 你應(yīng)該會(huì)更新外殼app. 為了防止用戶下載了不適合他現(xiàn)有外殼app的web內(nèi)容 - 你應(yīng)該設(shè)置 min_native_interface 這個(gè)值.

比如, 我們app里的chcp.json是這樣的:

<pre style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 16px; position: relative; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; white-space: pre-wrap; overflow-wrap: normal; overflow: auto; font-size: 13.6px; line-height: 1.45; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; word-break: normal; background-color: rgb(247, 247, 247);">{ "content_url": "https://5027caf9.ngrok.com", "release": "2015.09.01-13.30.35", "min_native_interface": 10}</pre>

外殼app的build版本是 13.

某個(gè)時(shí)候距潘,web內(nèi)容有了新的release發(fā)布:

<pre style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 16px; position: relative; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; white-space: pre-wrap; overflow-wrap: normal; overflow: auto; font-size: 13.6px; line-height: 1.45; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; word-break: normal; background-color: rgb(247, 247, 247);">{ "content_url": "https://5027caf9.ngrok.com", "release": "2015.09.05-12.20.15", "min_native_interface": 15}</pre>

插件加載到這段json的時(shí)候, 發(fā)現(xiàn) min_native_interface 比當(dāng)前外殼app的build號(hào)要大 - 它就不會(huì)下載web內(nèi)容. 而是觸發(fā)一個(gè) chcp_updateLoadFailed 錯(cuò)誤通知, 告訴用戶需要升級(jí)外殼app了. 更多內(nèi)容請(qǐng)看 從應(yīng)用商店請(qǐng)求app升級(jí) 小節(jié).

備注: 目前你還不能為不同平臺(tái)指定不同的 min_native_interface . 如果需要以后可以支持.

update

指定了什么時(shí)候安裝web內(nèi)容更新. 支持的值有:

  • start - app啟動(dòng)時(shí)安裝更新. 默認(rèn)值.

  • resume - app從后臺(tái)切換過(guò)來(lái)的時(shí)候安裝更新.

  • now - web內(nèi)容下載完畢即安裝更新.

你可以用JavaScript禁止自動(dòng)安裝. 請(qǐng)看 JavaScript module 小節(jié).

android_identifier

apk包名. 如果指定了 - 引導(dǎo)用戶到 Google Play Store 的app頁(yè)面.

ios_identifier

ios應(yīng)用標(biāo)識(shí)號(hào), 比如: id345038631. 如果指定了 - 引導(dǎo)用戶到 App Store 的app頁(yè)面.

Content manifest內(nèi)容清單

內(nèi)容清單描述了web項(xiàng)目所有文件的狀態(tài).

<pre style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 16px; position: relative; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; white-space: pre-wrap; overflow-wrap: normal; overflow: auto; font-size: 13.6px; line-height: 1.45; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; word-break: normal; background-color: rgb(247, 247, 247);">[
{ "file": "index.html", "hash": "5540bd44cbcb967efef932bc8381f886"
},
{ "file": "css/index.css", "hash": "e46d9a1c456a9c913ca10f3c16d50000"
},
{ "file": "img/logo.png", "hash": "7e34c95ac701f8cd9f793586b9df2156"
},
{ "file": "js/index.js", "hash": "0ba83df8459288fd1fa1576465163ff5"
}
]</pre>

根據(jù)它炼列,插件才知道什么文件被移除了, 什么文件更新或新增了. 于是:

  • 更新階段,從服務(wù)端下載所有web內(nèi)容文件;

  • 安裝階段音比,刪除服務(wù)端不存在(已移除)的文件.

這個(gè)文件應(yīng)該放在 www 目錄下俭尖,文件名是 chcp.manifest .這個(gè)文件也被打包到了外殼app內(nèi).

同樣的, 清單文件要放到 content_url (app配置 Application config中指定的)指定的目錄下. 比如, 如果你的 content_urlhttps://somedomain.com/www, 這個(gè)清單文件的url就必須是 https://somedomain.com/www/chcp.manifest.

生成 chcp.manifest 文件可以執(zhí)行命令行客戶端的 build 命令 (在cordova項(xiàng)目根目錄下執(zhí)行):

<pre style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 16px; position: relative; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; white-space: pre-wrap; overflow-wrap: normal; overflow: auto; font-size: 13.6px; line-height: 1.45; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; word-break: normal; background-color: rgb(247, 247, 247);">cordova-hcp build</pre>

file

相對(duì)于 www 的路徑(就是你存放web內(nèi)容的地方).

比如, 你的web內(nèi)容位于: /Workspace/Cordova/TestProject/www. 你的 file 值應(yīng)該是相對(duì)于這個(gè)路徑.

hash

文件的 MD5 值. 用于檢測(cè)自上次release以來(lái)。這個(gè)文件是否變更過(guò). 還有用于檢測(cè)app端下載的文件是否出錯(cuò).

建議: 每次變更web內(nèi)容后都應(yīng)該更新 chcp.manifest 文件. 否則插件不會(huì)檢測(cè)到任何更新.

Build options build設(shè)置

就像在 Cordova 配置項(xiàng) 一節(jié)中說(shuō)的 - 你可以在config.xml 文件里改變插件配置.

但是如果你想在使用build命令行的時(shí)候改變插件配置呢? 為了達(dá)到這個(gè)目的洞翩,你需要使用chcpbuild.options 文件.

文件必須位于 Cordova 項(xiàng)目根目錄. 在這個(gè)文件里面稽犁,你指定(JSON格式) 所有你想改變 config.xml 文件的配置. 源文件 config.xml (Cordova項(xiàng)目根目錄) 不會(huì)發(fā)生變動(dòng), 我們改變的是 特定平臺(tái)下的 config.xml (在cordova build過(guò)程的 after_prepare 階段).

比如, 你的Cordova項(xiàng)目是 /Cordova/TestProject 目錄.config.xml 文件 (/Cordova/TestProject/config.xml) 有下面的配置:

<pre style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 16px; position: relative; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; white-space: pre-wrap; overflow-wrap: normal; overflow: auto; font-size: 13.6px; line-height: 1.45; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; word-break: normal; background-color: rgb(247, 247, 247);"><chcp>
<config-file url="https://company_server.com/mobile/www/chcp.json" />
</chcp></pre>

這時(shí)我們?cè)?/Cordova/Testproject/ 下創(chuàng)建 chcpbuild.options 文件,文件內(nèi)容如下:

<pre style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 16px; position: relative; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; white-space: pre-wrap; overflow-wrap: normal; overflow: auto; font-size: 13.6px; line-height: 1.45; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; word-break: normal; background-color: rgb(247, 247, 247);">{
"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"
}
}</pre>

build app的時(shí)候, 轉(zhuǎn)為開發(fā)要用的服務(wù)器, 可執(zhí)行:

<pre style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 16px; position: relative; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; white-space: pre-wrap; overflow-wrap: normal; overflow: auto; font-size: 13.6px; line-height: 1.45; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; word-break: normal; background-color: rgb(247, 247, 247);">cordova build -- chcp-dev</pre>

結(jié)果就是, 特定拍下的 config.xml 文件(比如, /Cordova/TestProject/platforms/android/res/xml/config.xml) 變成了這樣:

<pre style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 16px; position: relative; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; white-space: pre-wrap; overflow-wrap: normal; overflow: auto; font-size: 13.6px; line-height: 1.45; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; word-break: normal; background-color: rgb(247, 247, 247);"><chcp>
<config-file url="https://dev.company_server.com/mobile/www/chcp.json"/>
</chcp></pre>

你可能注意到了 - 我們用的命令有個(gè) chcp-. 這個(gè)必須, 這樣插件才知道, 這個(gè)參數(shù)是為它設(shè)置的. 而且, 不會(huì)和其它插件的命令參數(shù)沖突.

如果你的app可以測(cè)試了 - 你可以用下面的命令build, 就指定了測(cè)試服務(wù)器:

<pre style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 16px; position: relative; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; white-space: pre-wrap; overflow-wrap: normal; overflow: auto; font-size: 13.6px; line-height: 1.45; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; word-break: normal; background-color: rgb(247, 247, 247);">cordova build -- chcp-QA</pre>

特定平臺(tái)下的 config.xml 就會(huì)變成:

<pre style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 16px; position: relative; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; white-space: pre-wrap; overflow-wrap: normal; overflow: auto; font-size: 13.6px; line-height: 1.45; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; word-break: normal; background-color: rgb(247, 247, 247);"><chcp>
<config-file url="https://test.company_server.com/mobile/www/chcp.json"/>
</chcp></pre>

當(dāng)我們需要上架app的時(shí)候 (Google Play, App Store) - 我們正常build:

<pre style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 16px; position: relative; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; white-space: pre-wrap; overflow-wrap: normal; overflow: auto; font-size: 13.6px; line-height: 1.45; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; word-break: normal; background-color: rgb(247, 247, 247);">cordova build --release</pre>

這樣 config.xml 是不會(huì)改變的.

如果沒有使用 chcpbuild.options - 插件會(huì)使用 config.xml 里面默認(rèn)的值.

JavaScript 模塊

默認(rèn)情況下, 所有的 檢查更新->下載->安裝 過(guò)程都是插件在原生端自動(dòng)進(jìn)行的. 不需要其它js端代碼. 然而, 這些過(guò)程也可以用js控制.

你可以:

  • 監(jiān)聽更新相關(guān)的事件;

  • 從服務(wù)端檢查和下載新的web內(nèi)容;

  • 安裝已下載的web內(nèi)容;

  • 更改插件配置;

  • 讓用戶到應(yīng)用商店下載新的外殼app.

監(jiān)聽更新事件

比如, web內(nèi)容已經(jīng)下載并可以安裝了骚亿,會(huì)有事件通知, 或者出錯(cuò)了導(dǎo)致安裝新的web內(nèi)容失敗了.

監(jiān)聽事件像這樣:

<pre style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 16px; position: relative; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; white-space: pre-wrap; overflow-wrap: normal; overflow: auto; font-size: 13.6px; line-height: 1.45; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; word-break: normal; background-color: rgb(247, 247, 247);"> document.addEventListener(eventName, eventCallback, false);

function eventCallback(eventData) {
// do something
}</pre>

錯(cuò)誤事件有詳細(xì)錯(cuò)誤信息. 像這樣:

<pre style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 16px; position: relative; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; white-space: pre-wrap; overflow-wrap: normal; overflow: auto; font-size: 13.6px; line-height: 1.45; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; word-break: normal; background-color: rgb(247, 247, 247);">function eventCallback(eventData) {
var error = eventData.details.error;
if (error) {
console.log('Error with code: ' + error.code);
console.log('Description: ' + error.description);
}
}</pre>

可用的事件如下:

  • chcp_updateIsReadyToInstall - web內(nèi)容已經(jīng)下載并可以安裝時(shí)觸發(fā).

  • chcp_updateLoadFailed - 插件無(wú)法下載web更新時(shí)觸發(fā). 詳細(xì)錯(cuò)誤信息在事件參數(shù)里.

  • chcp_nothingToUpdate - 無(wú)可用更新下載時(shí)觸發(fā).

  • chcp_updateInstalled - web內(nèi)容安裝成功時(shí)觸發(fā).

  • chcp_updateInstallFailed - web內(nèi)容安裝失敗時(shí)觸發(fā). 詳細(xì)錯(cuò)誤信息在事件參數(shù)里.

  • chcp_nothingToInstall -無(wú)可用更新安裝時(shí)觸發(fā).

  • chcp_assetsInstalledOnExternalStorage - 插件成功把a(bǔ)pp內(nèi)置的web內(nèi)容拷貝到外置存儲(chǔ)中時(shí)觸發(fā). 你可能需要開發(fā)調(diào)試時(shí)用到這個(gè)事件已亥,也許不會(huì).

  • chcp_assetsInstallationError -插件無(wú)法拷貝app內(nèi)置的web內(nèi)容到外置存儲(chǔ)中時(shí)觸發(fā). 如果此事件發(fā)生了 - 插件不再工作. 也許是設(shè)備沒有足夠的存儲(chǔ)空間導(dǎo)致. 詳細(xì)錯(cuò)誤信息在事件參數(shù)里.

該舉一些簡(jiǎn)單的例子了. 假設(shè)我們有個(gè) index.js 文件, 它被 index.html引用.

<pre style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 16px; position: relative; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; white-space: pre-wrap; overflow-wrap: normal; overflow: auto; font-size: 13.6px; line-height: 1.45; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; word-break: normal; background-color: rgb(247, 247, 247);">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();</pre>

這個(gè)和cordova默認(rèn)創(chuàng)建的 index.js 文件很像. 監(jiān)聽 chcp_updateIsReadyToInstall 事件如下:

<pre style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 16px; position: relative; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; white-space: pre-wrap; overflow-wrap: normal; overflow: auto; font-size: 13.6px; line-height: 1.45; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; word-break: normal; background-color: rgb(247, 247, 247);">bindEvents: function() {
// ...some other events subscription code...

document.addEventListener('chcp_updateIsReadyToInstall', this.onUpdateReady, false);
},</pre>

編寫事件處理函數(shù):

<pre style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 16px; position: relative; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; white-space: pre-wrap; overflow-wrap: normal; overflow: auto; font-size: 13.6px; line-height: 1.45; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; word-break: normal; background-color: rgb(247, 247, 247);">// chcp_updateIsReadyToInstall Event Handler
onUpdateReady: function() {
console.log('Update is ready for installation');
}</pre>

index.js 結(jié)果如下:

<pre style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 16px; position: relative; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; white-space: pre-wrap; overflow-wrap: normal; overflow: auto; font-size: 13.6px; line-height: 1.45; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; word-break: normal; background-color: rgb(247, 247, 247);">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();</pre>

這樣我們就知道了web內(nèi)容什么時(shí)候下載完畢并可以安裝了. 通過(guò) JavaScript 模塊我們可以讓插件即時(shí)安裝web更新, 否則將在下次啟動(dòng)app時(shí)安裝.

檢查更新

使用js代碼,讓插件檢查更新:

<pre style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 16px; position: relative; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; white-space: pre-wrap; overflow-wrap: normal; overflow: auto; font-size: 13.6px; line-height: 1.45; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; word-break: normal; background-color: rgb(247, 247, 247);">chcp.fetchUpdate(updateCallback);

function updateCallback(error, data) {
// do some work
}</pre>

回調(diào)有2個(gè)參數(shù):

  • error - 如果檢查失敗来屠,有error參數(shù); null 表示一切正常;

  • data - 額外的 數(shù)據(jù), 原生端提供. 暫時(shí)可以忽略.

我們假設(shè) index.html 有一些按鈕, 按下它可以檢查更新. 我們需要這樣寫代碼:

  1. 監(jiān)聽button的 click 事件.

  2. 當(dāng)點(diǎn)擊button時(shí)調(diào)用chcp.fetchUpdate() .

  3. 處理更新事件的結(jié)果.

我們來(lái)改 index.js 代碼:

<pre style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 16px; position: relative; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; white-space: pre-wrap; overflow-wrap: normal; overflow: auto; font-size: 13.6px; line-height: 1.45; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; word-break: normal; background-color: rgb(247, 247, 247);">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();</pre>

注意: 即使你在fetchUpdate 回調(diào)里處理了虑椎,相關(guān)的更新事件還是會(huì)觸發(fā)并廣播的.

安裝web更新

調(diào)用:

<pre style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 16px; position: relative; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; white-space: pre-wrap; overflow-wrap: normal; overflow: auto; font-size: 13.6px; line-height: 1.45; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; word-break: normal; background-color: rgb(247, 247, 247);">chcp.installUpdate(installationCallback);

function installationCallback(error) {
// do some work
}</pre>

如果安裝失敗 - error 參數(shù)會(huì)有錯(cuò)誤詳細(xì)信息. 否則- 為 null.

現(xiàn)在讓我們來(lái)繼續(xù)上面的代碼,處理web內(nèi)容下載完后的安裝.

<pre style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 16px; position: relative; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; white-space: pre-wrap; overflow-wrap: normal; overflow: auto; font-size: 13.6px; line-height: 1.45; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; word-break: normal; background-color: rgb(247, 247, 247);">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();</pre>

注意: 即使你在 installUpdate 回調(diào)里處理了俱笛,相關(guān)的更新事件還是會(huì)觸發(fā)并廣播的

運(yùn)行時(shí)改變插件設(shè)置

正常情況下捆姜,所有的插件配置都在 config.xml. 但是你可以用js動(dòng)態(tài)改變.

通過(guò)下面的代碼實(shí)現(xiàn):

<pre style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 16px; position: relative; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; white-space: pre-wrap; overflow-wrap: normal; overflow: auto; font-size: 13.6px; line-height: 1.45; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; word-break: normal; background-color: rgb(247, 247, 247);">chcp.configure(options, callback);

function callback(error) {
// do some work
}</pre>

支持的有:

  • config-file - application config(chcp.json) 的url. 如果設(shè)置了 - 這個(gè)url將會(huì)被用于檢查更新,而不是config.xml中的值.

  • auto-download - 設(shè)為 false 你可以禁止插件自動(dòng)檢測(cè)web內(nèi)容更新并下載.

  • auto-install - 設(shè)為 false 你可以禁止插件自動(dòng)安裝web更新.

這些需要在 deviceready 事件中設(shè)置. 你應(yīng)該在每個(gè)頁(yè)面加載的時(shí)候處理,

假如你一開就打算手動(dòng)更新和下載安裝 - 你應(yīng)該在config.xml中設(shè)置

<pre style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 16px; position: relative; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; white-space: pre-wrap; overflow-wrap: normal; overflow: auto; font-size: 13.6px; line-height: 1.45; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; word-break: normal; background-color: rgb(247, 247, 247);"><chcp>
<auto-download enabled="false" />
<auto-install enabled="false" />
</chcp></pre>

而不是js端動(dòng)態(tài)設(shè)置.

比如, 我們?cè)赾onfig.xml禁用了 auto-download and auto-install . 然后某個(gè)時(shí)間點(diǎn) config-file 改變了, 但是我們不想從原有的url檢測(cè)和下載web更新. 此時(shí), 我們應(yīng)該這樣:

  1. 發(fā)布新版本的web內(nèi)容, 它們可以用于最初的 config-file url.

  2. 在新的版本 index.js 文件中迎膜,內(nèi)容像這樣:

    <pre style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 16px; position: relative; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; white-space: pre-wrap; overflow-wrap: normal; overflow: auto; font-size: 13.6px; line-height: 1.45; color: rgb(0, 0, 0); word-break: normal; background-color: rgb(247, 247, 247);">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();</pre>

引導(dǎo)用戶去應(yīng)用商店更新外殼app

Application config app配置 小節(jié)我們知道泥技,可以給web更新設(shè)置最小支持的外殼app版本 (min_native_interface ). 如果插件檢查發(fā)現(xiàn)用戶安裝的外殼app版本比服務(wù)端新的web內(nèi)容要求的版本要低 - 就會(huì)觸發(fā)錯(cuò)誤事件,錯(cuò)誤碼chcp.error.APPLICATION_BUILD_VERSION_TOO_LOW. 通過(guò)這個(gè)錯(cuò)誤碼我們可以引導(dǎo)用戶去應(yīng)用商店更新外殼app (Google Play /App Store).

這里你想怎么做就怎么做. 常用方法是顯示一個(gè)對(duì)話框磕仅,問(wèn)用戶是否需要轉(zhuǎn)到應(yīng)用商店. 插件也提供了這個(gè).

你需要做的是:

  1. 在 application config(chcp.json) 中設(shè)置t android_identifierios_identifier.

  2. js端監(jiān)聽相應(yīng)事件零抬,并在出現(xiàn)錯(cuò)誤的時(shí)候調(diào)用 chcp.requestApplicationUpdate 方法.

舉個(gè)例子. 簡(jiǎn)單起見我們監(jiān)聽 chcp_updateLoadFailed 事件.

<pre style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 16px; position: relative; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; white-space: pre-wrap; overflow-wrap: normal; overflow: auto; font-size: 13.6px; line-height: 1.45; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial; word-break: normal; background-color: rgb(247, 247, 247);">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();</pre>

錯(cuò)誤碼

下載安裝web更新的過(guò)程中可能會(huì)發(fā)生一些錯(cuò)誤. 你可以從回調(diào)或者事件中匹配錯(cuò)誤碼( chcp.error 對(duì)象中有各種錯(cuò)誤碼).

v1.2.0版本之前 你需要用特定的錯(cuò)誤碼數(shù)字值. 此時(shí)開始, 請(qǐng)使用靜態(tài)常量名镊讼,這樣可以使代碼可讀宽涌,也可以減少對(duì)錯(cuò)誤碼具體數(shù)字的依賴. 比如, 不應(yīng)該用 if (error.code == -2) 而用 if (error.code == chcp.error.APPLICATION_BUILD_VERSION_TOO_LOW).

錯(cuò)誤列表:

  • NOTHING_TO_INSTALL - 請(qǐng)求插件安裝更新平夜,卻沒有更新需要安裝. 值為 1.

  • NOTHING_TO_UPDATE - 沒有可用web更新需要下載.值為 2.

  • FAILED_TO_DOWNLOAD_APPLICATION_CONFIG - 下載新的application config 文件(chcp.json)失敗. 要么文件不存在或者網(wǎng)絡(luò)問(wèn)題.值為 -1.

  • APPLICATION_BUILD_VERSION_TOO_LOW - 外殼app的build版本號(hào)太低. 新的web內(nèi)容需要新的外殼app. 用戶需要更新外殼app.值為 -2.

  • FAILED_TO_DOWNLOAD_CONTENT_MANIFEST - 下載內(nèi)容清單文件(chcp.manifest)失敗. 文件chcp.manifest 必須位于 content_url 對(duì)應(yīng)目錄下, 和chcp.json一起.值為 -3.

  • FAILED_TO_DOWNLOAD_UPDATE_FILES - 下載web內(nèi)容失敗. 清單 chcp.manifest 中列出文件的必須都要位于 content_url 對(duì)應(yīng)目錄下. 還有, 檢查各個(gè)文件的MD5是否正確. 值為 -4.

  • FAILED_TO_MOVE_LOADED_FILES_TO_INSTALLATION_FOLDER - 移動(dòng)已下載的文件到安裝目錄時(shí)失敗. 可能存儲(chǔ)空間不足.值為 -5.

  • UPDATE_IS_INVALID - web內(nèi)容已損壞. 安裝之前,插件會(huì)檢查已下載文件的MD5和 chcp.manifest 中的比較看是否一致. 如果不一致或者文件缺失 - 會(huì)發(fā)生此錯(cuò)誤. 值為 -6.

  • FAILED_TO_COPY_FILES_FROM_PREVIOUS_RELEASE - 從上一版本拷貝www下文件到新版本www目錄出錯(cuò).可能存儲(chǔ)空間不足.值為 -7.

  • FAILED_TO_COPY_NEW_CONTENT_FILES - 拷貝新文件到內(nèi)容目錄下失敗.可能存儲(chǔ)空間不足.值為 -8.

  • LOCAL_VERSION_OF_APPLICATION_CONFIG_NOT_FOUND - 加載本地chcp.json失敗. 可能是用戶手動(dòng)刪除了外部存儲(chǔ)的web內(nèi)容相關(guān)文件. 如果發(fā)生卸亮,會(huì)回滾至上移release版本的web內(nèi)容.值為 -9.

  • LOCAL_VERSION_OF_MANIFEST_NOT_FOUND -加載本地chcp.manifest失敗.可能是用戶手動(dòng)刪除了外部存儲(chǔ)的web內(nèi)容相關(guān)文件. 如果發(fā)生忽妒,會(huì)回滾至上移release版本的web內(nèi)容. 值為 -10.

  • LOADED_VERSION_OF_APPLICATION_CONFIG_NOT_FOUND -加載本地已下載的新版本的chcp.json失敗.可能是用戶手動(dòng)刪除了外部存儲(chǔ)的web內(nèi)容相關(guān)文件.如果發(fā)生 - app下次啟動(dòng)時(shí)會(huì)恢復(fù). 值為 -11.

  • LOADED_VERSION_OF_MANIFEST_NOT_FOUND -加載本地已下載的新版本的chcp.manifest失敗.可能是用戶手動(dòng)刪除了外部存儲(chǔ)的web內(nèi)容相關(guān)文件.如果發(fā)生 - app下次啟動(dòng)時(shí)會(huì)恢復(fù).值為 -12.

  • FAILED_TO_INSTALL_ASSETS_ON_EXTERNAL_STORAGE - 拷貝app內(nèi)置web內(nèi)容到外部存儲(chǔ)時(shí)失敗.可能存儲(chǔ)空間不足. app初次啟動(dòng)時(shí)會(huì)執(zhí)行此操作. 如果失敗,插件就不再有用了. 值為 -13.

  • CANT_INSTALL_WHILE_DOWNLOAD_IN_PROGRESS - 調(diào)用 chcp.installUpdate 而 插件正在下載更新時(shí)觸發(fā). 你必須等待下載完畢. 值為 -14.

  • CANT_DOWNLOAD_UPDATE_WHILE_INSTALLATION_IN_PROGRESS - 調(diào)用 chcp.fetchUpdate 而安裝過(guò)程在再執(zhí)行. 你必須等待安裝完畢. 值為 -15.

  • INSTALLATION_ALREADY_IN_PROGRESS - 調(diào)用 chcp.installUpdate,而安裝過(guò)程在再執(zhí)行.值為 -16.

  • DOWNLOAD_ALREADY_IN_PROGRESS - 調(diào)用 chcp.fetchUpdate,而 插件正在下載更新時(shí)觸發(fā). 值為 -17.

  • ASSETS_FOLDER_IN_NOT_YET_INSTALLED - 調(diào)用 chcp 方法, 而插件正在拷貝app內(nèi)置web內(nèi)容到外部存儲(chǔ)時(shí)觸發(fā). 只可能在app初次啟動(dòng)時(shí)發(fā)生. 最后這個(gè)錯(cuò)誤會(huì)被移除.值為 -18.


關(guān)于熱更新的流程解析

好多同學(xué)都測(cè)試不成功兼贸,大家不要想太復(fù)雜了段直,我再簡(jiǎn)要概括一下:

  1. chcp.json文件中的content_url為服務(wù)器項(xiàng)目的地址加端口號(hào)
  1. config.xml為服務(wù)器項(xiàng)目地址加端口號(hào)再加上/chcp.json
  1. 每次修改完文件后,必須將【修改的文件】和【chcp.manifest文件】一并復(fù)制到服務(wù)器項(xiàng)目中進(jìn)行覆蓋溶诞。
  1. 將服務(wù)器中的chcp.json文件中的【"release": "2016.08.04-18.04.06"】時(shí)間改為當(dāng)前時(shí)間鸯檬。
3 和 4是最重要的,不然熱更新就不起作用螺垢。

最后你們不要在糾結(jié)cordova-hcp server喧务,這個(gè)東西就是在開發(fā)的時(shí)候啟動(dòng)用來(lái)監(jiān)聽文件的修改,如果有文件修改枉圃,就對(duì)應(yīng)在chcp.manifest中修改該文件的hash值功茴。

還沒完,為了更清楚的了解熱更新是怎么回事孽亲,這里我畫了一張圖坎穿。

[熱更新的流程解析]

[圖片上傳中...(image-5f8fb-1557555367321-2)]

  1. app啟動(dòng)
  1. 從服務(wù)器請(qǐng)求chcp.json文件(會(huì)覆蓋本地chcp.json文件)。
  1. 服務(wù)器返回chcp.json文件與app里的chcp.json文件做對(duì)比返劲,判斷兩個(gè)文件中的release時(shí)間玲昧。
  1. 如果服務(wù)器chcp.json文件的release時(shí)間大于app里chcp.json的release時(shí)間(說(shuō)明新的資源)
  1. 如果有新的資源,再次發(fā)送一個(gè)請(qǐng)求篮绿,請(qǐng)求服務(wù)器的chcp.manifest文件(會(huì)覆蓋本地chcp.json文件)孵延。
  1. 服務(wù)器返回chcp.manifest文件與app里的chcp.manifest文件內(nèi)容做對(duì)比。
  1. 如果有不一樣的hash值搔耕。
  1. 對(duì)服務(wù)器請(qǐng)求新的資源隙袁。
  1. 請(qǐng)求成功的資源將覆蓋本地資源。

案例

這里通過(guò)對(duì)app進(jìn)行抓包弃榨,來(lái)分析熱更新是怎樣進(jìn)行應(yīng)用內(nèi)更新的菩收。注意看1~8,我是沒有對(duì)服務(wù)器資源進(jìn)行更新的鲸睛。直到第9個(gè)請(qǐng)求的時(shí)候:9娜饵,10,11連續(xù)發(fā)送了3個(gè)請(qǐng)求官辈。

[熱更新的抓包圖]

[圖片上傳中...(image-f38240-1557555367321-1)]

  • 第9個(gè)請(qǐng)求將服務(wù)器的chcp.json文件請(qǐng)求回來(lái)后判斷時(shí)間是大于app的chcp.json時(shí)間的
  • 然后發(fā)送了第10個(gè)請(qǐng)求箱舞,chcp.manifest遍坟,與本地chcp.manifest文件做對(duì)比
  • 其中我只改了一個(gè)login.html,所以這里對(duì)login.html重新加載覆蓋晴股。

一直有人問(wèn)我愿伴,用不了,不會(huì)用之類的。我在這說(shuō)一下

[圖片上傳中...(image-83354d-1557555367321-0)]

用 cordova-hcp server 命令啟動(dòng) hcp 服務(wù)器的時(shí)候,會(huì)看到如上圖的local server 和public server前方,這兩個(gè)地址是不能自己定義的

類似“https://f5f6894c.ngrok.io” 這個(gè)地址貌似并沒有用碟联,也許只是國(guó)內(nèi)沒法用吧
所以,我建議不要用 cordova-hcp server,你需要自己部署一個(gè)服務(wù)器,托管 www 下的 web 內(nèi)容
Local Development Add-on 這個(gè)擴(kuò)展也可以不要,記得自己生成新的apk/ipa之前要改config.xml的version(android-versionCode/ios-CFBundleVersion)就行了

其實(shí) cordova-hcp server 啟動(dòng)的那個(gè)服務(wù)器幻妓,就是多了2個(gè)功能:
1、檢測(cè)到 www 變更后劫拢,自動(dòng)生成清單文件 chcp.manifest
2肉津、自動(dòng)實(shí)時(shí)推送變更到app端

用了你自己的服務(wù)器之后,這2個(gè)功能都沒了尚镰。所以
1阀圾、每次更改 www 下的 web 內(nèi)容之后,一定要手動(dòng)用 cordova-hcp build(在corodva項(xiàng)目根目錄下執(zhí)行)狗唉, 生成清單文件 chcp.manifest
2初烘、app 只能在每次啟動(dòng)的時(shí)候,才能檢查有無(wú)內(nèi)容更新分俯。有更新就會(huì)在后臺(tái)下載肾筐,等到下次啟動(dòng) app 才應(yīng)用更新。(也就是要重啟app 2次才能看到效果)

chcp.json 這個(gè)文件的內(nèi)容也要改下缸剪,把 update 改為 "start"吗铐,比如

[javascript] view plaincopy

<embed id="ZeroClipboardMovie_1" src="https://csdnimg.cn/public/highlighter/ZeroClipboard.swf" loop="false" menu="false" quality="best" bgcolor="#ffffff" width="16" height="16" name="ZeroClipboardMovie_1" align="middle" allowscriptaccess="always" allowfullscreen="false" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" flashvars="id=1&width=16&height=16" wmode="transparent" style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 0px; overflow-wrap: break-word;">

<embed id="ZeroClipboardMovie_3" src="https://csdnimg.cn/public/highlighter/ZeroClipboard.swf" loop="false" menu="false" quality="best" bgcolor="#ffffff" width="16" height="16" name="ZeroClipboardMovie_3" align="middle" allowscriptaccess="always" allowfullscreen="false" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" flashvars="id=3&width=16&height=16" wmode="transparent" style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 0px; overflow-wrap: break-word;">

  1. {
  2. "update": "start",
  3. "content_url": "http://10.0.0.100/HCP/",
  4. "release": "2016.04.28-10.14.32"
  5. }
{  "update": "start",  "content_url": "http://10.0.0.100/HCP/",  "release": "2016.04.28-10.14.32"}

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

可以在 cordova 項(xiàng)目根目錄下放一個(gè) cordova-hcp.json杏节,這是個(gè)模板文件
這樣每次執(zhí)行 cordova-hcp build, 就會(huì)利用這個(gè)模板生成新的 chcp.json唬渗,而不用手動(dòng)更改 www/chcp.json了。
cordova-hcp.json內(nèi)容如下:

[javascript] view plaincopy

<embed id="ZeroClipboardMovie_2" src="https://csdnimg.cn/public/highlighter/ZeroClipboard.swf" loop="false" menu="false" quality="best" bgcolor="#ffffff" width="16" height="16" name="ZeroClipboardMovie_2" align="middle" allowscriptaccess="always" allowfullscreen="false" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" flashvars="id=2&width=16&height=16" wmode="transparent" style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 0px; overflow-wrap: break-word;">

<embed id="ZeroClipboardMovie_4" src="https://csdnimg.cn/public/highlighter/ZeroClipboard.swf" loop="false" menu="false" quality="best" bgcolor="#ffffff" width="16" height="16" name="ZeroClipboardMovie_4" align="middle" allowscriptaccess="always" allowfullscreen="false" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" flashvars="id=4&width=16&height=16" wmode="transparent" style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 0px; overflow-wrap: break-word;">

  1. {
  2. "update": "start",
  3. "content_url": "http://10.0.0.100/HCP/"
  4. }
{  "update": "start",  "content_url": "http://10.0.0.100/HCP/"}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末奋渔,一起剝皮案震驚了整個(gè)濱河市镊逝,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌嫉鲸,老刑警劉巖撑蒜,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡座菠,警方通過(guò)查閱死者的電腦和手機(jī)狸眼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)浴滴,“玉大人拓萌,你說(shuō)我怎么就攤上這事⊙灿ǎ” “怎么了司志?”我有些...
    開封第一講書人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)降宅。 經(jīng)常有香客問(wèn)我,道長(zhǎng)囚霸,這世上最難降的妖魔是什么腰根? 我笑而不...
    開封第一講書人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮拓型,結(jié)果婚禮上额嘿,老公的妹妹穿的比我還像新娘。我一直安慰自己劣挫,他們只是感情好册养,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著压固,像睡著了一般球拦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上帐我,一...
    開封第一講書人閱讀 49,730評(píng)論 1 289
  • 那天坎炼,我揣著相機(jī)與錄音,去河邊找鬼拦键。 笑死谣光,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的芬为。 我是一名探鬼主播萄金,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼媚朦!你這毒婦竟也來(lái)了氧敢?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤莲镣,失蹤者是張志新(化名)和其女友劉穎福稳,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瑞侮,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡的圆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年鼓拧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片越妈。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡季俩,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出梅掠,到底是詐尸還是另有隱情酌住,我是刑警寧澤,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布阎抒,位于F島的核電站酪我,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏且叁。R本人自食惡果不足惜都哭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望逞带。 院中可真熱鬧欺矫,春花似錦、人聲如沸展氓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)遇汞。三九已至未妹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間勺疼,已是汗流浹背教寂。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留执庐,地道東北人酪耕。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像轨淌,于是被迫代替她去往敵國(guó)和親迂烁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容