123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447 |
- // 热更新管理
- let HotUpdateMgr = {
- init () {
- if (!cc.sys.isNative) {
- return;
- }
- // 更新与热更新状态 JMC.UPDATE_STATUS
- // UNSTART: -1, // 未开始
- // DOWNLOADING: -1, // 下载中
- // DOWNLOAD_PAUSE: -1, // 下载暂停[暂未使用]
- // DOWNLOAD_FINISHED: -1, // 下载完成
- // DOWNLOAD_FAILED: -1 // 下载失败
- this.status = JMC.UPDATE_STATUS.UNSTART;
- this.stStatus = JMC.UPDATE_STATUS.UNSTART;
- // 缓存下载进度
- this.realTotalBytes = 0;
- this.downloadedBytes = 0;
- // 热更新资源下载到本地的路径,需要修改这里的路径需要同时修改main.js
- this._updatePath = G.LaunchMgr.getHotfixPath();
- // project.manifest默认路径,必须在resources中
- let UpdateCfg = require('UpdateCfg');
- this._localDefaultManifestPath = UpdateCfg.localManifestPath;
- // manifest的JSON对象,方便后续进行设置url等操作
- this.localManifestObj = {};
- this.checkResVersion();
- },
- /**
- * 开始更新
- *
- * @author libo
- * @date 2019-03-15
- * @returns
- am.getState()
- UNINITED 0
- UNCHECKED 1
- PREDOWNLOAD_VERSION 2
- DOWNLOADING_VERSION 3
- VERSION_LOADED 4
- PREDOWNLOAD_MANIFEST 5
- DOWNLOADING_MANIFEST 6
- MANIFEST_LOADED 7
- NEED_UPDATE 8
- READY_TO_UPDATE 9
- UPDATING 10
- UNZIPPING 11
- UP_TO_DATE 12
- FAIL_TO_UPDATE 13
- */
- gotoUpdate () {
- G.LogUtils.log('[HotUpdateMgr] gotoUpdate');
- // AssetsManager对象
- this.am = new jsb.AssetsManager('', this._updatePath, G.UpdateUtils.compareVersionStr);
- if (this.remoteManifest) {
- this.am.loadLocalManifest(this.localManifest, this._updatePath);
- this.am.loadRemoteManifest(this.remoteManifest);
- } else {
- this.am.loadLocalManifest(this.localManifest, this._updatePath);
- }
- // 判断是否初始化完毕
- if (!this.am.getLocalManifest() || !this.am.getLocalManifest().isLoaded()) {
- G.LogUtils.error('[HotUpdateMgr] gotoUpdate 加载manifest失败');
- this.am = undefined;
- return;
- }
- this.am.setMaxConcurrentTask(2);
- this.am.setEventCallback(this._updateCb.bind(this));
- this.am.update();
- },
- /**
- 更新进程回调
- * @author libo
- * @date 2019-03-15
- * @param {EventAssetsManagerEx} event
- event.getEventCode()
- ERROR_NO_LOCAL_MANIFEST 0
- ERROR_DOWNLOAD_MANIFEST 1
- ERROR_PARSE_MANIFEST 2
- NEW_VERSION_FOUND 3
- ALREADY_UP_TO_DATE 4
- UPDATE_PROGRESSION 5
- ASSET_UPDATED 6
- ERROR_UPDATING 7
- UPDATE_FINISHED 8
- UPDATE_FAILED 9
- ERROR_DECOMPRESS 10
- event.getMessage()
- event.getPercent()
- event.getPercentByFile()
- event.getDownloadedBytes()
- event.getTotalBytes()
- event.getDownloadedFiles()
- event.getTotalFiles()
- */
- _updateCb (event) {
- G.LogUtils.log('[HotUpdateMgr] updateCb Code:', event.getEventCode());
- if (!this.am) {
- return;
- }
- let status = undefined;
- let reason = undefined;
- let errorMsg = event.getMessage();
- switch (event.getEventCode()) {
- case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST: {
- // 没有本地配置,跳过更新
- status = JMC.UPDATE_STATUS.DOWNLOAD_FAILED;
- reason = 'ERROR_NO_LOCAL_MANIFEST:' + errorMsg;
- break;
- }
- case jsb.EventAssetsManager.ASSET_UPDATED: {
- // 忽略该状态,使用 UPDATE_PROGRESSION 即可
- return;
- }
- case jsb.EventAssetsManager.UPDATE_PROGRESSION: {
- // 更新进度
- let totalBytes = event.getTotalBytes();
- let downloadedBytes = event.getDownloadedBytes();
- if (totalBytes != 0) {
- downloadedBytes += this.realTotalBytes - event.getTotalBytes();
- }
- if (downloadedBytes > 0) {
- this.downloadedBytes = downloadedBytes;
- }
- let downloadedMb = G.UpdateUtils.bytesToMb(this.downloadedBytes).toFixed(1);
- let totalMb = G.UpdateUtils.bytesToMb(totalBytes).toFixed(1);
- G.LogUtils.log(`[HotUpdateMgr] updateCb totalBytes: ${totalBytes} totalMb: ${totalMb}Mb downloadedBytes: ${
- this.downloadedBytes
- } downloadedMb: ${downloadedMb}Mb`);
- status = JMC.UPDATE_STATUS.DOWNLOADING;
- reason = 'UPDATE_PROGRESSION';
- break;
- }
- case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST: {
- // 下载manifest失败,跳过更新
- status = JMC.UPDATE_STATUS.DOWNLOAD_FAILED;
- reason = 'ERROR_DOWNLOAD_MANIFEST:' + errorMsg;
- break;
- }
- case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST: {
- // 解析manifest失败,跳过更新
- status = JMC.UPDATE_STATUS.DOWNLOAD_FAILED;
- reason = 'ERROR_PARSE_MANIFEST:' + errorMsg;
- break;
- }
- case jsb.EventAssetsManager.ALREADY_UP_TO_DATE: {
- // 已经更新到最终版本,跳过更新
- status = JMC.UPDATE_STATUS.DOWNLOAD_FINISHED;
- reason = 'ALREADY_UP_TO_DATE';
- break;
- }
- case jsb.EventAssetsManager.UPDATE_FINISHED: {
- // 更新完成,需要重启
- status = JMC.UPDATE_STATUS.DOWNLOAD_FINISHED;
- reason = 'UPDATE_FINISHED';
- break;
- }
- case jsb.EventAssetsManager.UPDATE_FAILED: {
- // 更新失败,需要断点续传
- // (服务器需要支持 Accept-Ranges,否则从头开始下载)
- // this.am.downloadFailedAssets();
- status = JMC.UPDATE_STATUS.DOWNLOAD_FAILED;
- reason = 'UPDATE_FINISHED:' + errorMsg;
- break;
- }
- case jsb.EventAssetsManager.ERROR_UPDATING: {
- // 部分资源下载失败
- status = JMC.UPDATE_STATUS.DOWNLOAD_FAILED;
- reason = 'ERROR_UPDATING:' + errorMsg;
- break;
- }
- case jsb.EventAssetsManager.ERROR_DECOMPRESS: {
- // 解压缩失败
- status = JMC.UPDATE_STATUS.DOWNLOAD_FAILED;
- reason = 'ERROR_DECOMPRESS:' + errorMsg;
- break;
- }
- default: {
- // 忽略其它状态
- return;
- }
- }
- this.status = status;
- // 统计状态更新
- this._stStatusChange(status, reason);
- // 更新结束的处理
- if (status == JMC.UPDATE_STATUS.DOWNLOAD_FINISHED) {
- this.am.setEventCallback(undefined);
- this.am = undefined;
- this.needRestart = true;
- this.updateResVersion();
- cc.game.emit('e_mgr_launch_update_finished');
- } else if (status == JMC.UPDATE_STATUS.DOWNLOAD_FAILED) {
- this.am.setEventCallback(undefined);
- this.am = undefined;
- cc.game.emit('e_mgr_launch_update_failed');
- } else if (status == JMC.UPDATE_STATUS.DOWNLOADING) {
- cc.game.emit('e_mgr_launch_update_progression');
- }
- },
- /**
- * 加载本地manifest
- *
- * @author libo
- * @date 2019-03-15
- * @returns {object} manifest的JSON对象
- */
- loadLocalManifestObj () {
- let localManifestPath = this._updatePath + '/project.manifest';
- G.LogUtils.log('[HotUpdateMgr] loadLocalManifestObj localManifestPath', localManifestPath);
- // 热更新路径没有manifest就去读resources的manifest
- if (!jsb.fileUtils.isFileExist(localManifestPath)) {
- localManifestPath = cc.url.raw(this._localDefaultManifestPath);
- // resources没有就返回空对象
- if (!jsb.fileUtils.isFileExist(localManifestPath)) {
- G.LogUtils.error('[HotUpdateMgr] loadLocalManifestObj 配置错误', localManifestPath);
- return {};
- }
- }
- // 读取manifest并转换成json对象
- let str = jsb.fileUtils.getStringFromFile(localManifestPath);
- return JSON.parse(str);
- },
- /**
- * 替换manifest对象
- *
- * @author libo
- * @date 2019-03-21
- * @param {json} obj
- */
- updateLocalManifestObj (obj) {
- this.localManifestObj = obj;
- this.localManifest = new jsb.Manifest(JSON.stringify(obj), this._updatePath);
- },
- /**
- * 将远程manifest load 进AssetsManage中
- *
- * @author libo
- * @date 2019-03-25
- * @param {json} obj
- */
- loadRemoteManifestObj (obj) {
- this.remoteManifest = new jsb.Manifest(JSON.stringify(obj), this._updatePath);
- },
- /**
- * 替换manifest对象
- *
- * @author libo
- * @date 2019-03-21
- * @param {String} remoteManifestStr
- */
- updateRemoteManifest (remoteManifestStr) {
- // 计算更新大小
- let localManifest = this.localManifestObj;
- let remoteManifest = JSON.parse(remoteManifestStr);
- this.realTotalBytes = this._calculateUpdateSize(localManifest, remoteManifest);
- // 存储manifest
- let cacheManifestPath = this._getHotfixTempManifestPath();
- jsb.fileUtils.writeStringToFile(remoteManifestStr, cacheManifestPath);
- // 加载热更新的配置到内存
- this.loadRemoteManifestObj(remoteManifest);
- },
- // 加载缓存的manifest
- loadTempManifest () {
- let cacheManifestPath = this._getHotfixTempManifestPath();
- if (jsb.fileUtils.isFileExist(cacheManifestPath)) {
- let remoteManifestStr = jsb.fileUtils.getStringFromFile(cacheManifestPath);
- this.updateRemoteManifest(remoteManifestStr);
- }
- },
- // 获取缓存的manifest md5
- getTempManifestMd5 () {
- // 缓存manifest路径
- let cacheManifestPath = this._getHotfixTempManifestPath();
- if (jsb.fileUtils.isFileExist(cacheManifestPath)) {
- let remoteManifestStr = jsb.fileUtils.getStringFromFile(cacheManifestPath);
- let hexHash = MD5.hex(remoteManifestStr);
- return hexHash;
- } else {
- return '';
- }
- },
- // 热更新完毕后重启应用
- restartApp () {
- if (this.didRestart) {
- return;
- }
- this.didRestart = true;
- // 在本地储存一个标志,用于闪屏界面判断是否需要跳过
- cc.sys.localStorage.setItem('HOT_UPDATE_SKIP_SPLASH', true);
- cc.audioEngine.stopAll();
- cc.game.restart();
- },
- // 判断是否下载成功
- isDownloadFinished () {
- return this.status === JMC.UPDATE_STATUS.DOWNLOAD_FINISHED;
- },
- // 判断是否下载失败
- isDownloadFailed () {
- return this.status === JMC.UPDATE_STATUS.DOWNLOAD_FAILED;
- },
- // 更新资源号并持久化
- updateResVersion () {
- let curResVersion = G.RemoteUpdateInfoMgr.remoteUpdateInfo.newResVer;
- G.RES_VERSION = curResVersion;
- G.LogUtils.log('[HotUpdateMgr] updateResVersion 热更新完成,当前资源号', G.RES_VERSION);
- cc.sys.localStorage.setItem('HOT_UPDATE_RES_VERSION', curResVersion);
- },
- // 更新资源号
- checkResVersion () {
- G.LogUtils.log('[HotUpdateMgr] checkResVersion: 包体的G.RES_VERSION', G.RES_VERSION);
- // 上次热更新成功的包体内的代码版本号
- let oldAppVersion = cc.sys.localStorage.getItem('HOT_UPDATE_VERSION_NAME');
- // 上次热更新成功的包体内的混淆码
- let oldHxCode = cc.sys.localStorage.getItem('HOT_UPDATE_HX_CODE');
- // 当前包体内的代码版本号
- let curAppVersion = G.APP_VERSION;
- // 当前包体内的混淆码
- let curHxCode = G.HX_CODE;
- // 当前包体内的代码资源号
- let curResVersion = G.RES_VERSION;
- G.LogUtils.log('[HotUpdateMgr] checkResVersion: oldAppVersion', oldAppVersion, 'oldHxCode', oldHxCode);
- G.LogUtils.log('[HotUpdateMgr] checkResVersion: curAppVersion', curAppVersion, 'curHxCode', curHxCode);
- // 更新本地的资源号
- if ((oldAppVersion == curAppVersion) && (oldHxCode == curHxCode)) {
- // 上次热更新成功的资源号
- let resVersion = cc.sys.localStorage.getItem('HOT_UPDATE_RES_VERSION');
- if (resVersion) {
- if (typeof(resVersion) === 'string')
- resVersion = parseInt(resVersion);
- if (resVersion > curResVersion)
- G.RES_VERSION = resVersion;
- }
- } else {
- // 版本更新了,清除旧的热更新文件
- this.clearOldHotfixPath();
- this.clearOldHotfixTempPath();
- cc.sys.localStorage.setItem('HOT_UPDATE_VERSION_NAME', curAppVersion);
- cc.sys.localStorage.setItem('HOT_UPDATE_HX_CODE', curHxCode);
- cc.sys.localStorage.setItem('HOT_UPDATE_RES_VERSION', curResVersion);
- }
- G.LogUtils.log('[HotUpdateMgr] checkResVersion: 热更新前的G.RES_VERSION', G.RES_VERSION);
- },
- // 删除旧热更新路径下资源
- clearOldHotfixPath () {
- G.LogUtils.log('[HotUpdateMgr] 清空热更新目录');
- let tempDir = this._updatePath;
- jsb.fileUtils.removeDirectory(tempDir);
- jsb.fileUtils.createDirectory(tempDir);
- },
- // 删除热更新temp路径下资源
- clearOldHotfixTempPath () {
- G.LogUtils.log('[HotUpdateMgr] 清空热更新临时目录');
- let tempDir = this._getHotfixTempPath();
- jsb.fileUtils.removeDirectory(tempDir);
- jsb.fileUtils.createDirectory(tempDir);
- },
- // 统计 热更新状态更新
- _stStatusChange (status, reason) {
- if (this.stStatus === status) {
- return;
- }
- this.stStatus = status;
- },
- // 获取热更新temp目录
- _getHotfixTempPath () {
- return G.LaunchMgr.getHotfixTempPath();
- },
- // 热更新临时manifest路径
- _getHotfixTempManifestPath () {
- return jsb.fileUtils.getWritablePath() + 'cache.manifest';
- },
- /**
- * 比对manifest,计算更新大小
- *
- * @author libo
- * @date 2019-03-26
- * @param {json} localManifest 本地manifest
- * @param {json} remoteManifest 远程manifest
- * @returns {number} 更新大小,单位字节
- */
- _calculateUpdateSize (localManifest, remoteManifest) {
- let remoteManifestAssets = remoteManifest.assets;
- let localManifestAssets = localManifest.assets;
- let size = 0;
- for (let key in remoteManifestAssets) {
- let remoteAsset = remoteManifestAssets[key];
- let localAsset = localManifestAssets[key];
- if (!localAsset || localAsset.md5 != remoteAsset.md5) {
- size += remoteAsset.size;
- }
- }
- return size;
- },
- removeHotfixTempManifestPath () {
- let cacheManifestPath = this._getHotfixTempManifestPath();
- if (jsb.fileUtils.isFileExist(cacheManifestPath)) {
- jsb.fileUtils.removeFile(cacheManifestPath);
- }
- }
- };
- module.exports = HotUpdateMgr;
|