首页 > 代码库 > cocos2d-js 在线更新代码脚本 动态更新脚本程序 热更新

cocos2d-js 在线更新代码脚本 动态更新脚本程序 热更新

一、cocos2d-js 动态更新的基本思路

动态更新的好处不言而喻,不需要重新上架审核,能节省很多时间,也能让用户尽快使用上最新的版本,减少下载的成本。

  • 官方BETA版本后提供了AssetsManager类,可以完成动态更新的步骤,说明:https://github.com/chukong/cocos-docs/blob/master/manual/framework/html5/v3/assets-manager/zh.md
  • cocos2d程序安装后,以Android为例,程序存在于2个地方:apk安装目录(/data/dalvik-cache),apk数据目录(/data/data/[包名])
  • AssetsManager根据projec.manifest文件的配置,把新文件下载到apk数据目录,并默认把这个下载目录设置为最优先搜索的地方。(api:)
  • project.json文件中指定的js文件,将在程序main.js启动前就加载完。main.js不需要写到这个list中。所以需要动态更新的js,不能列在这个json中
  • 除了main.js外,把其他js列到一个文件中:src/jsList.js。AssetsManager检查完之后,先加载这个jsList.js,然后根据里边的配置再加载全部js。

二、程序发布步骤

本文参考:https://github.com/faint2death/cocos2d-js/blob/master/assetsmanager.md,但配置的方式不一样,本文更偏于使用官方的配置。按参考文章的写法,更新多次之后,project.manifest文件会很大,这影响用户更新的速度。

1、修改main.js,加载AssetsManager功能

cc.game.onStart = function(){     cc.view.setDesignResolutionSize(800, 450, cc.ResolutionPolicy.SHOW_ALL);     cc.view.resizeWithBrowserSize(true);         var failCount = 0;     var maxFailCount = 1;   //最大错误重试次数    /**      * 自动更新js和资源      */     var AssetsManagerLoaderScene = cc.Scene.extend({         _am:null,         _progress:null,         _percent:0,         run:function(){             if (!cc.sys.isNative) {                 this.loadGame();                 return;             }            var layer = new cc.Layer();             this.addChild(layer);             this._progress = new cc.LabelTTF.create("update 0%", "Arial", 12);             this._progress.x = cc.winSize.width / 2;             this._progress.y = cc.winSize.height / 2 + 50;             layer.addChild(this._progress);            var storagePath = (jsb.fileUtils ? jsb.fileUtils.getWritablePath() : "./");            this._am = new jsb.AssetsManager("res/project.manifest", storagePath);             this._am.retain();            if (!this._am.getLocalManifest().isLoaded())             {                 cc.log("Fail to update assets, step skipped.");                 this.loadGame();             }             else             {                 var that = this;                 var listener = new cc.EventListenerAssetsManager(this._am, function(event) {                     switch (event.getEventCode()){                         case cc.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:                             cc.log("No local manifest file found, skip assets update.");                             that.loadGame();                             break;                         case cc.EventAssetsManager.UPDATE_PROGRESSION:                             that._percent = event.getPercent();                             cc.log(that._percent + "%");                             var msg = event.getMessage();                             if (msg) {                                 cc.log(msg);                             }                             break;                         case cc.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:                         case cc.EventAssetsManager.ERROR_PARSE_MANIFEST:                             cc.log("Fail to download manifest file, update skipped.");                             that.loadGame();                             break;                         case cc.EventAssetsManager.ALREADY_UP_TO_DATE:                             cc.log("ALREADY_UP_TO_DATE.");                             that.loadGame();                             break;                         case cc.EventAssetsManager.UPDATE_FINISHED:                             cc.log("Update finished.");                             that.loadGame();                             break;                         case cc.EventAssetsManager.UPDATE_FAILED:                             cc.log("Update failed. " + event.getMessage());                             failCount++;                             if (failCount < maxFailCount)                             {                                 that._am.downloadFailedAssets();                             }                             else                             {                                 cc.log("Reach maximum fail count, exit update process");                                 failCount = 0;                                 that.loadGame();                             }                             break;                         case cc.EventAssetsManager.ERROR_UPDATING:                             cc.log("Asset update error: " + event.getAssetId() + ", " + event.getMessage());                             that.loadGame();                             break;                         case cc.EventAssetsManager.ERROR_DECOMPRESS:                             cc.log(event.getMessage());                             that.loadGame();                             break;                         default:                             break;                     }                 });                cc.eventManager.addListener(listener, 1);                 this._am.update();                 cc.director.runScene(this);             }            this.schedule(this.updateProgress, 0.5);         },        loadGame:function(){             //jsList是jsList.js的变量,记录全部js。             cc.loader.loadJs(["src/jsList.js"], function(){                 cc.loader.loadJs(jsList, function(){                     cc.director.runScene(new MainScene());                 });             });         },        updateProgress:function(dt){             this._progress.string = "update" + this._percent + "%";         },        onExit:function(){             cc.log("AssetsManager::onExit");            this._am.release();             this._super();         }     });         var scene = new AssetsManagerLoaderScene();     scene.run(); }; cc.game.run();

 

2、建立jsList.js。使用固定名字jsList,这个跟第1步的代码相对应。

var jsList = [     "src/resource.js",     "src/app.js" ]

 

3、修改project.json。加入extensions模块,删除jsList的内容。

{     "project_type": "javascript",    "debugMode" : 1,     "showFPS" : true,     "frameRate" : 60,     "id" : "gameCanvas",     "renderMode" : 0,     "engineDir":"frameworks/cocos2d-html5",    "modules" : ["cocos2d", "extensions"],    "jsList" : [              ] }

 

 

4、项目res目录增加一个project.manifest文件,AssetsManager.js里会用到。url填写自己服务器的地址,packageUrl是准备动态更新的文件的存放目录。

{     "packageUrl" : "http://192.168.1.11:8000/res",     "remoteManifestUrl" : "http://192.168.1.11:8000/res/project.manifest",     "remoteVersionUrl" : "http://192.168.1.11:8000/res/version.manifest",     "version" : "1.0.1",     "engineVersion" : "3.0 rc0",     "assets" : {    },    "searchPaths" : [     ] }

 

5、打包程序。此时即使没有网络,也已经可以运行基础版本。

 

三、动态更新测试

1、服务器放置version.manifest和新的project.manifest。

AssetsManager会先检查version.manifest,判断是否有更新。如果有,再拉取project.manifest。可以说version.manifest就是缩小版的project.manifest,只有头几行,两者一致。

version.manifest:

{     "packageUrl" : "http://192.168.1.11:8000/res",     "remoteManifestUrl" : "http://192.168.1.11:8000/res/project.manifest",     "remoteVersionUrl" : "http://192.168.1.11:8000/res/version.manifest",     "version" : "1.0.1",     "engineVersion" : "3.0 rc0" }

 

project.manifest:

{     "packageUrl" : "http://192.168.1.11:8000/res",     "remoteManifestUrl" : "http://192.168.1.11:8000/res/project.manifest",     "remoteVersionUrl" : "http://192.168.1.11:8000/res/version.manifest",     "version" : "1.0.1",     "engineVersion" : "3.0 rc0",     "assets" : {         "src/app.zip" : {             "md5" : "D07D260D8072F786A586A6A430D0E98B",             "compressed" : true         }     },    "searchPaths" : [     ] }

 

manifest这里使用了官方说明没有提到的compressed,src/app.zip并没有在初始打包的程序中,这个只是更新用的。指定了compressed=true,AssetsManager下载后会自动解压这个文件,并保留这个文件。这样就可以减少网络传输的文件大小。

app.zip压缩的是app.js,解压后将覆盖初始化安装的app.js,从而实现了动态更新。

这里可以多次更新,不断更新version号即可,每次AssetsManager会检查文件是否存在、文件md5是否一致,如果不存在或者md5不一致都会重新下载。

 

2、无需重新打包发布,直接打开cocos2d程序,可以看到update的字样,如果打开了logcat,也可以看到对应的日志。