App端的升級,又分為整包更新和資源熱更新兩種:
1、整包更新,即常規的整個App安裝包重新下載安裝。
2、資源熱更新,即App不重新安裝,里面的js等前端代碼進行更新。
一、整包更新方案
1、IOS更新
一般iOS Appstore的安裝包,無法直接更新。App啟動后檢查有新版本,只能跳轉到Appstore,然后用戶在Appstore的詳情頁點擊更新按鈕。
2、Android更新
而Android App,可以直接下載新的apk,只要包名和證書不變,就可以覆蓋安裝。
注意:
(1)App的升級檢測代碼必須使用條件編譯,否則在非App環境由於不存在plus相關API,將會報錯。
(2)升級地址URL,如果是自行托管的App,就提供自己的包地址。如果是打開應用市場,那URL如下:
if (plus.os.name=="Android") { appurl = "market://details?id=io.dcloud.hellouniapp";
//這個是通用應用市場,如果想指定某個應用商店,需要單獨查這個應用商店的包名或scheme及參數 } else { appurl = "itms-apps://itunes.apple.com/cn/app/hello-uni-app/id1417078253"; }
(3)版本檢測需要打包app,真機運行基座無法測試。因為真機運行的plus.runtime.version是固定值。
關於升級,比較省事的就是跳轉到瀏覽器下載apk包,下載完成之后安裝。另外可以在應用內下載,下載完成之后調用plus.runtime.install安裝。具體代碼如下:
// 在index.vue 中的onload方法里面或者app.vue中的onLaunch中添加如下部分:
update() { var _this = this; uni.request({ url: `${this.$store.state.apiBaseUrl}/users/versions`, //請求接口
method: 'POST', success: result => { if (result.data.code == 1) { plus.runtime.getProperty(plus.runtime.appid, function(inf) { if(inf.version != result.data.data.versions){ uni.showModal({ title: "發現新版本", content: "確認下載更新", success: (res) => { if (res.confirm == true) {//當用戶確定更新,執行更新
_this.doUpData(); } } }) } }); } }, }) }, doUpData() { uni.showLoading({ title: '更新中……' }) uni.downloadFile({//執行下載
url: '***', //下載地址
success: downloadResult => {//下載成功
uni.hideLoading(); if (downloadResult.statusCode == 200) { uni.showModal({ title: '', content: '更新成功,確定現在重啟嗎?', confirmText: '重啟', confirmColor: '#EE8F57', success: function(res) { if (res.confirm == true) { plus.runtime.install(//安裝
downloadResult.tempFilePath, { force: true }, function(res) { utils.showToast('更新成功,重啟中'); plus.runtime.restart(); } ); } } }); } } }); }
二、資源在線升級(熱更新)
HBuilderX 1.6.5 起,uni-app 支持生成 App 資源升級包。
1、生成 App 資源升級包
(1)修改版本號:
首先,更新 manifest.json 中的版本號。比如之前是 1.0.0,那么新版本應該是 1.0.1 或 1.1.0 這樣。

(2)發行:
然后,在 HBuilderX 中生成wgt的升級包(wgt):菜單->發行->原生App-制作移動App資源升級包
生成結束會在控制台告知升級包的輸出位置。
2、安裝資源升級包
應用的升級需要服務端與客戶端配合完成,下面以本地測試過程中的操作舉例說明:
(1)存放資源
將 %appid%.wgt 文件存放在服務器的 static 目錄下,如即 http://www.example.com/static/UNI832D722.wgt。
(2)服務端接口
約定檢測升級的接口,如地址為:http://www.example.com/update/
(3)傳入參數
參數名 | 類型 | 默認值 | 說明 |
---|---|---|---|
name | String | '' | 客戶端讀取到的應用名稱,定義這個參數可以方便多個應用復用接口。 |
version | String | '' | 客戶端讀取到的版本號信息 |
(4)返回參數
參數名 | 類型 | 默認值 | 說明 |
---|---|---|---|
update | Boolean | false | 是否有更新 |
wgtUrl | String | '' | wgt 包的下載地址,用於 wgt 方式更新。 |
pkgUrl | String | '' | apk/ipa 包的下載地址或 AppStore 地址,用於整包升級的方式。 |
(5)代碼示例
下面是一個簡單的服務端判定的示例,僅做參考,實際開發中根據自身業務需求處理。
var express = require('express'); var router = express.Router(); var db = require('./db'); // TODO 查詢配置文件或者數據庫信息來確認是否有更新
function checkUpdate(params, callback) { db.query('一段SQL', function(error, result) { // 這里簡單判定下,不相等就是有更新。
var currentVersions = params.appVersion.split('.'); var resultVersions = result.appVersion.split('.'); if (currentVersions[0] < resultVersions[0]) { // 說明有大版本更新
callback({ update: true, wgtUrl: '', pkgUrl: result.pkgUrl }) } else { // 其它情況均認為是小版本更新
callback({ update: true, wgtUrl: result.wgtUrl, pkgUrl: '' }) } }); } router.get('/update/', function(req, res) { var appName = req.query.name; var appVersion = req.query.version; checkUpdate({ appName: appName, appVersion: appVersion }, function(error, result) { if (error) { throw error; } res.json(result); }); });
注意事項
- 以上約定,僅做參考。
- 服務端的具體判定邏輯,請根據自身的業務邏輯靈活處理。
- 應用中的路徑盡量不要包含特殊符號
3、客戶端檢測升級
在 App.vue 的 onLaunch 中檢測升級,代碼如下:
// #ifdef APP-PLUS
plus.runtime.getProperty(plus.runtime.appid, function(widgetInfo) { uni.request({ url: 'http://www.example.com/update/', data: { version: widgetInfo.version, name: widgetInfo.name }, success: (result) => { var data = result.data; if (data.update && data.wgtUrl) { uni.downloadFile({ url: data.wgtUrl, success: (downloadResult) => { if (downloadResult.statusCode === 200) { plus.runtime.install(downloadResult.tempFilePath, { force: false }, function() { console.log('install success...'); plus.runtime.restart(); }, function(e) { console.error('install fail...'); }); } } }); } } }); }); // #endif
4、不支持的情況
(1)SDK 部分有調整,比如新增了 Maps 模塊等,不可通過此方式升級,必須通過整包的方式升級。
(2)原生插件的增改,同樣不能使用此方式。
(3)對於老的非自定義組件編譯模式,這種模式已經被淘汰下線。但以防萬一也需要說明下,老的非自定義組件編譯模式,如果之前工程沒有 nvue 文件,但更新中新增了 nvue 文件,不能使用此方式。因為非自定義組件編譯模式如果沒有nvue文件是不會打包weex引擎進去的,原生引擎無法動態添加。自定義組件模式默認就含着weex引擎,不管工程下有沒有nvue文件。
5、注意事項
(1)條件編譯,僅在 App 平台執行此升級邏輯。
(2)appid 以及版本信息等,在 HBuilderX 真機運行開發期間,均為 HBuilder 這個應用的信息,因此需要打包自定義基座或正式包測試升級功能。
(3)plus.runtime.version 或者 uni.getSystemInfo() 讀取到的是 apk/ipa 包的版本號,而非 manifest.json 資源中的版本信息,所以這里用 plus.runtime.getProperty() 來獲取相關信息。
(4)安裝 wgt 資源包成功后,必須執行 plus.runtime.restart(),否則新的內容並不會生效。
(5)如果App的原生引擎不升級,只升級wgt包時需要注意測試wgt資源和原生基座的兼容性。平台默認會對不匹配的版本進行提醒,如果自測沒問題,可以在manifest中配置忽略提示,詳見https://ask.dcloud.net.cn/article/35627
6、關於熱更新是否影響應用上架
應用市場為了防止開發者不經市場審核許可,給用戶提供違法內容,對熱更新大多持排斥態度。
但實際上熱更新使用非常普遍,不管是原生開發中還是跨平台開發。
Apple曾經禁止過jspatch,但沒有打擊其他的熱更新方案,包括cordovar、react native、DCloud。封殺jspatch其實是因為jspatch有嚴重安全漏洞,可以被黑客利用,造成三方黑客可篡改其他App的數據。
使用熱更新需要注意:
- 上架審核期間不要彈出熱更新提示
- 熱更新內容使用https下載,避免被三方網絡劫持
- 不要更新違法內容、不要通過熱更新破壞應用市場的利益,比如iOS的虛擬支付要老老實實給Apple分錢
如果你的應用沒有犯這些錯誤,應用市場是不會管的。