123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393 |
- // 远程更新信息管理
- let RemoteUpdateInfoMgr = {
- init () {
- if (!cc.sys.isNative) {
- return;
- }
- // 版本更新信息缓存
- this.remoteUpdateInfo = {};
- // 是否已准备好远程更新信息
- this.isReadyRemoteInfo = false;
- // 是否已准备好热更新
- this.isReadyHotUpdate = false;
- // 是否版本更新
- this.isVersionUpdate = false;
- // 是否下载完成了
- this.isDownloadFinish = false;
- // 是否热更新
- this.isHotUpdate = false;
- // 是否小资源热更新
- this.isSmallHotUpdate = false;
- // 是否大资源热更新
- this.isBigHotUpdate = false;
- },
- /**
- * 已准备好远程更新信息
- *
- * @author Pyden
- * @date 2020-02-21
- */
- didReadyRemoteInfo () {
- this.isReadyRemoteInfo = true;
- },
- /**
- * 已准备好热更新
- *
- * @author Pyden
- * @date 2020-02-21
- */
- didReadyHotUpdate () {
- this.isReadyHotUpdate = true;
- if (this.isHotUpdate) {
- // 确定大小更新类型
- this._checkHotUpdateType();
- // 小包或WiFi时,自动开始更新
- if (this.isSmallHotUpdate || cc.sys.getNetworkType() == cc.sys.NetworkType.LAN) {
- G.HotUpdateMgr.gotoUpdate();
- }
- }
- },
- /**
- * 根据更新大小判断更新类型
- *
- * @author libo
- * @date 2019-03-26
- */
- _checkHotUpdateType () {
- let updateMb = G.UpdateUtils.bytesToMb(G.HotUpdateMgr.realTotalBytes).toFixed(1);
- G.LogUtils.log('需要更新', updateMb, 'Mb', ' 小资源上限', this.remoteUpdateInfo.newSmallResLimit, 'Mb');
- if (updateMb > this.remoteUpdateInfo.newSmallResLimit) {
- G.LogUtils.log('大资源更新');
- this.isSmallHotUpdate = false;
- this.isBigHotUpdate = true;
- } else {
- G.LogUtils.log('小资源更新');
- this.isSmallHotUpdate = true;
- this.isBigHotUpdate = false;
- }
- },
- /**
- * 检查热更新
- *
- * @author Pyden
- * @date 2020-02-21
- * @returns
- */
- _checkHotUpdate () {
- let needUpdate = false;
- // 本函数将影响以下状态。先重置
- this.isHotUpdate = false;
- this.isReadyHotUpdate = false;
- this.isSmallHotUpdate = false;
- this.isBigHotUpdate = false;
- // 热更新判断
- if (this.remoteUpdateInfo.newResVer) {
- let lv = G.RES_VERSION.toString();
- let sv = this.remoteUpdateInfo.newResVer;
- G.LogUtils.log('RemoteUpdateInfoMgr._checkHotUpdate version', lv);
- G.LogUtils.log('RemoteUpdateInfoMgr._checkHotUpdate newResVer', sv);
- needUpdate = G.UpdateUtils.compareVersionStr(lv, sv) < 0;
- if (needUpdate) {
- this.isHotUpdate = true;
- // 本地manifest读取放到这里是为了兼容模拟器加载资源,放到构造函数中模拟器会读取资源失败
- let localManifestObj = G.HotUpdateMgr.loadLocalManifestObj();
- // 修改local manifest
- localManifestObj.packageUrl = this.remoteUpdateInfo.newResUrl;
- localManifestObj.remoteManifestUrl = cc.path.join(this.remoteUpdateInfo.newResUrl, 'project.manifest');
- localManifestObj.remoteVersionUrl = cc.path.join(this.remoteUpdateInfo.newResUrl, 'version.manifest');
- G.HotUpdateMgr.updateLocalManifestObj(localManifestObj);
- // lv 不超过 lastForceVer 的需要强更
- let lastForceVer = this.remoteUpdateInfo.lastForceVer;
- this.remoteUpdateInfo.newResForce = lastForceVer >= lv;
- let md5 = this.remoteUpdateInfo.newResFlistMd5;
- let remoteMd5 = G.HotUpdateMgr.getTempManifestMd5();
- if (md5 == remoteMd5) {
- G.HotUpdateMgr.loadTempManifest();
- this.didReadyHotUpdate();
- } else {
- G.LogUtils.error('检查本地缓存manifest: md5不匹配', md5, remoteMd5);
- // 需要先加载localManifestObj
- this.requestRemoteManifest();
- }
- }
- } else {
- // 后台无新版热更新信息
- this.didReadyHotUpdate();
- }
- return needUpdate;
- },
- /**
- * 检查版本更新
- *
- * @author Pyden
- * @date 2020-02-21
- */
- _checkVersionUpdate () {
- let needUpdate = false;
- // 本函数将影响以下状态。先重置
- this.isVersionUpdate = false;
- this.isDownloadFinish = false;
- // 版本更新判断
- if (this.remoteUpdateInfo.newAppVer) {
- let lv = G.MiddleDevice.getVersionName();
- let sv = this.remoteUpdateInfo.newAppVer;
- G.LogUtils.log('RemoteUpdateInfoMgr._checkVersionUpdate version', lv);
- G.LogUtils.log('RemoteUpdateInfoMgr._checkVersionUpdate newAppVer', sv);
- needUpdate = G.UpdateUtils.compareVersionStr(lv, sv) < 0;
- if (needUpdate) {
- // 强制更新必定不是静默更新
- if (this.remoteUpdateInfo.newAppForce) {
- this.remoteUpdateInfo.newAppSilence = false;
- }
- let isDownloadFinish = false;
- let isSupportUpdate = G.VersionUpdateMgr.isSupportUpdate();
- if (isSupportUpdate) {
- isDownloadFinish = G.VersionUpdateMgr.checkDownloaded(
- this.remoteUpdateInfo.newAppUrl,
- this.remoteUpdateInfo.newAppMd5
- );
- // 当APK没下载完成且是静默下载时,启动版本更新下载
- if (!isDownloadFinish && this.remoteUpdateInfo.newAppSilence) {
- G.VersionUpdateMgr.setSilenceDownload(true);
- G.VersionUpdateMgr.setWifiOnlyDownload(true);
- G.VersionUpdateMgr.download(this.remoteUpdateInfo.newAppUrl, this.remoteUpdateInfo.newAppMd5);
- }
- }
- this.isVersionUpdate = true;
- this.isDownloadFinish = isDownloadFinish;
- }
- }
- return needUpdate;
- },
- /**
- * 组装本地版本信息
- *
- * @author libo
- * @date 2019-03-20
- * @returns {url} 请求地址,地址中包含参数
- */
- _packLocalUpdateInfo () {
- // appVer 0: string #客户端当前APK版本
- // resVer 1: string #客户端当前脚本版本
- // channel 2: integer #渠道号
- // platform 3: integer #android=1,iOS=2
- // hxCode 4: integer #混淆码
- let appVer = G.MiddleDevice.getVersionName();
- let resVer = G.RES_VERSION;
- let hxCode = G.HX_CODE;
- let channel = G.MiddleDevice.getChannelId();
- let platform = G.LaunchMgr.getPatform();
- let param = `version_update?platform=${platform}&channel=${channel}&appVer=${appVer}&resVer=${resVer}&hxCode=${hxCode}`;
- let url = G.NetworkMgr.getParamsUrl();
- let remoteURL = cc.path.join(url, param);
- G.LogUtils.log('remoteURL ' + remoteURL);
- return remoteURL;
- },
- /**
- * 请求版本服务器,获取版本信息用于判断热更新和版本更新
- *
- * @author libo
- * @date 2019-03-20
- * callback
- * ok: 是否请求成功
- * errorMsg: 请求失败时的错误信息
- */
- requestRemoteUpdateInfo (callback) {
- // 本函数将影响以下状态。先重置
- this.isReadyRemoteInfo = false;
- this.isReadyHotUpdate = false;
- this.isVersionUpdate = false;
- this.isDownloadFinish = false;
- this.isHotUpdate = false;
- this.isSmallHotUpdate = false;
- this.isBigHotUpdate = false;
- G.LogUtils.log('RemoteUpdateInfoMgr.requestRemoteUpdateInfo');
- let updateCfg = require('UpdateCfg');
- let xhr = cc.loader.getXMLHttpRequest();
- xhr.timeout = updateCfg.remoteInfoTimeout;
- xhr.onload = (e) => {
- G.LogUtils.log('RemoteUpdateInfoMgr.requestRemoteUpdateInfo response', xhr.response);
- if (xhr.status == 200) {
- this._responseRemoteUpdateInfo(true, undefined, JSON.parse(xhr.response), callback);
- } else {
- this._responseRemoteUpdateInfo(false, '网络错误', undefined, callback);
- }
- };
- xhr.ontimeout = (e) => {
- G.LogUtils.error('RemoteUpdateInfoMgr.requestRemoteUpdateInfo 请求超时');
- this._responseRemoteUpdateInfo(false, '请求超时', undefined, callback);
- };
- xhr.onerror = (e) => {
- G.LogUtils.error('RemoteUpdateInfoMgr.requestRemoteUpdateInfo 请求报错');
- this._responseRemoteUpdateInfo(false, '请求报错', undefined, callback);
- };
- xhr.open('GET', this._packLocalUpdateInfo(), true);
- // 设置期望的返回数据类型 'json' 'text' 'document' ...
- xhr.responseType = 'text';
- // 设置请求头
- xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
- xhr.send();
- },
- /**
- * 请求回调
- *
- * @author Pyden
- * @date 2020-02-21
- * @param {Boolean} ok 是否请求成功
- * @param {String} errorMsg 请求失败时的错误信息
- * @param {Object} response 请求成功时的返回信息
- * @param {Function} callback 回调
- */
- _responseRemoteUpdateInfo (ok, errorMsg, response, callback) {
- // code 0: integer
- // sysTime 1: integer #服务器时间
- // newAppVer 2: string #版本更新,最新APK版本
- // newAppUrl 3: string #版本更新,app下载url
- // newAppMd5 4: string #版本更新,app的md5
- // newAppForce 5: boolean #版本更新,是否强制下载
- // newAppSilence 6: boolean #版本更新,是否静默下载
- // newAppSize 7: string #版本更新,app大小
- // newAppAward 8: string #版本更新,奖励文案
- // newAppBanner 9: string #版本更新,背景图路径
- // newAppContent 10: string #版本更新,附加信息(暂未使用)
- // newResVer 11: integer #热更新,客户端当前版本APK对应的 最新脚本版本
- // newResUrl 12: string #热更新,res下载url
- // newResFlistMd5 13: string #热更新,flist的md5
- // newResForce 14: boolean #热更新,是否强制下载
- // newSmallResLimit 15: integer #热更新小资源上限 单位是M
- // newResBanner 16: string #热更新,背景图路径
- // lastForceVer 17: string #最后一次强制热更新资源号
- if (ok) {
- this.remoteUpdateInfo = response.params;
- G.LogUtils.log('RemoteUpdateInfoMgr._responseRemoteUpdateInfo remoteUpdateInfo', this.remoteUpdateInfo);
- if (!cc.sys.isNative) {
- this.didReadyRemoteInfo();
- this.didReadyHotUpdate();
- return;
- }
- let needHotUpdate = this._checkHotUpdate();
- if (!needHotUpdate) {
- this._checkVersionUpdate();
- }
- }
- this.didReadyRemoteInfo();
- if (callback) {
- callback(ok, errorMsg);
- }
- },
- /**
- * 通过http请求下载远程manifest
- *
- * @author libo
- * @date 2019-03-26
- */
- requestRemoteManifest () {
- // 本函数将影响以下状态。先重置
- this.isSmallHotUpdate = false;
- this.isBigHotUpdate = false;
- // 先清空临时文件,避免之前的更新污染
- G.HotUpdateMgr.clearOldHotfixTempPath();
- // 获取新的远程manifest
- let url = G.HotUpdateMgr.localManifestObj.remoteManifestUrl;
- G.LogUtils.log('RemoteUpdateInfoMgr.requestRemoteManifest', url);
- let xhr = cc.loader.getXMLHttpRequest();
- xhr.timeout = 5000;
- xhr.onload = (e) => {
- G.LogUtils.log('RemoteUpdateInfoMgr.requestRemoteManifest response', xhr.response);
- if (xhr.status == 200) {
- this._responseRemoteManifest(true, undefined, xhr.response);
- } else {
- this._responseRemoteManifest(false, '网络错误', undefined);
- }
- };
- xhr.ontimeout = (e) => {
- G.LogUtils.error('RemoteUpdateInfoMgr.requestRemoteManifest 请求超时');
- this._responseRemoteManifest(false, '请求超时', undefined);
- };
- xhr.onerror = (e) => {
- G.LogUtils.error('RemoteUpdateInfoMgr.requestRemoteManifest 请求报错');
- this._responseRemoteManifest(false, '请求报错', undefined);
- };
- xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
- xhr.open('GET', url, true);
- // 设置期望的返回数据类型 'json' 'text' 'document' ...
- xhr.responseType = 'text';
- // 设置请求头
- xhr.send();
- },
- /**
- * 请求回调
- *
- * @author Pyden
- * @date 2020-02-21
- * @param {Boolean} ok 是否请求成功
- * @param {String} errorMsg 请求失败时的错误信息
- * @param {JSONString} response 请求成功时的返回信息
- * @returns
- */
- _responseRemoteManifest (ok, errorMsg, response) {
- if (ok) {
- G.LogUtils.log('RemoteUpdateInfoMgr._responseRemoteManifest', response);
- let hexHash = MD5.hex(response);
- let newResFlistMd5 = this.remoteUpdateInfo.newResFlistMd5;
- if (hexHash == newResFlistMd5) {
- G.HotUpdateMgr.updateRemoteManifest(response);
- } else {
- G.LogUtils.error('RemoteUpdateInfoMgr._responseRemoteManifest MD5不匹配', newResFlistMd5, hexHash);
- // 加载Manifest失败,不开启热更新
- this.isHotUpdate = false;
- }
- } else {
- // 加载Manifest失败,不开启热更新
- this.isHotUpdate = false;
- }
- this.didReadyHotUpdate();
- }
- };
- module.exports = RemoteUpdateInfoMgr;
|