求助,热更新一次隔一次会把从远程下载下来的目录内容清空

我刚刚开始弄热更新,基本是照搬了案例教程里面的代码,生成manifest的工具是从扩展商量下来的热更新插件

现在遇到一个很奇怪的问题,我设置了一个缓存目录
this._storagePath = ((jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/') + 'GHGame');
将远程服务器上的文件下载到该目录,
当我第一次运行程序的时候,运行正常,会检测到更新,然后下载文件。
重启app以后,检测到已经是最新版本,但是,此时,缓存目录下的文件会被全部清除,当我下次再打开App的时候,又会重新检测到有新版本,然后下载到该目录下。
然后就会如此往复,一次提示有更新,下载文件,重启app后,提示已经是最新版本,然后将目录清空。

下面是我使用的代码,请高手看看
初始AssetsManager

private InitAssetsManager()
{
        //设置热更新的目录
        this._storagePath = ((jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/') + 'GHGame');
        //console.log('Storage path for remote asset : ' + this._storagePath);
        this._am = new jsb.AssetsManager("", this._storagePath, this.VersionCompareHandle);
        if (!cc.sys.ENABLE_GC_FOR_NATIVE_OBJECTS) {
            this._am.retain();
        }
        this._am.setVerifyCallback(function (path, asset)
        {
            // When asset is compressed, we don't need to check its md5, because zip file have been deleted.
            var compressed = asset.compressed;
            // Retrieve the correct md5 value.
            var expectedMD5 = asset.md5;
            // asset.path is relative path and path is absolute.
            var relativePath = asset.path;
            // The size of asset file, but this value could be absent.
            var size = asset.size;
            if (compressed) {
                //panel.info.string = "Verification passed : " + relativePath;
                return true;
            }
            else {
                //panel.info.string = "Verification passed : " + relativePath + ' (' + expectedMD5 + ')';
                return true;
            }
        });
        if (cc.sys.os === cc.sys.OS_ANDROID) 
        {
            // Some Android device may slow down the download process when concurrent tasks is too much.
            // The value may not be accurate, please do more test and find what's most suitable for your game.
            this._am.setMaxConcurrentTask(2);
        }
}

由于我需要动态变更更新网址,所有写了一个处理本地manifest的代码
处理本地manifest

private HandleLocalManifest()
    {
        //读取原始Manifest
        let filestring;
        if (jsb.fileUtils.isFileExist(this._storagePath + "/originProject.manifest")) 
        {
            filestring = jsb.fileUtils.getStringFromFile(this._storagePath + "/originProject.manifest");
        }
        else 
        {
            filestring = jsb.fileUtils.getStringFromFile(this.originManifestUrl);
        }
        console.log(filestring);
        
        //转换成json
        let obj = JSON.parse(filestring);
        //拼接远程3个远程url
        //let url=GameRuntimeData.GetInstance().url;
        let url = "http://127.0.0.1/GHGame/hotUpdateServer/GHGame";
        obj.packageUrl = url + obj.packageUrl;
        obj.remoteManifestUrl = url + obj.remoteManifestUrl;
        obj.remoteVersionUrl = url + obj.remoteVersionUrl;
        this._manifestObj = obj;
        //保存成本地文件
        let afterString = JSON.stringify(obj);
        console.log(afterString);
        jsb.fileUtils.writeStringToFile(afterString, this._storagePath + "/project.manifest")
        this._am.loadLocalManifest(this._storagePath + "/project.manifest");
    }

然后就是检查更新和执行更新
检查更新 和 检查的监听

CheckUpdate()
    {
        this._checkListener = new jsb.EventListenerAssetsManager(this._am, this.checkCb.bind(this));
        cc.eventManager.addListener(this._checkListener, 1);

        this._am.checkUpdate();
        this._updating = true;
    }

checkCb(event) 
    {
        cc.log('Code: ' + event.getEventCode());

        let skipUpdate:boolean=false;
        let startUpdate:boolean=false;

        switch (event.getEventCode())
        {
            case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST://获取本地manifest失败
                console.log("No local manifest file found, hot update skipped.");
                skipUpdate=true;
                break;
            case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST://下载远程manifest失败
            case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST://解析远程manifest失败
                console.log("Fail to download manifest file, hot update skipped.");
                skipUpdate=true;
                break;
            case jsb.EventAssetsManager.ALREADY_UP_TO_DATE://已经是最新版本
                console.log("Already up to date with the latest remote version,hot update skipped");
                skipUpdate=true;
                break;
            case jsb.EventAssetsManager.NEW_VERSION_FOUND://发现新版本
                console.log('New version found, please try to update.');
                startUpdate=true;
                break;
            default:
                return;
        }
        
        cc.eventManager.removeListener(this._checkListener);
        this._checkListener = null;
        this._updating = false;

        if (skipUpdate) 
        {
            //发送跳过更新消息
            MsgSystem.GetInstance().PostMsg(MsgDefine.hotUpdate_skip);
        }

        if (startUpdate) 
        {
            //发送开始更新消息
            MsgSystem.GetInstance().PostMsg(MsgDefine.hotUpdate_start);
            //开始更新
            this.StartUpdate();
        }
        
    }

如果检查到有新版本,就会进行开始更新
开始更新 和 更新的监听

StartUpdate()
    {
        //开始更新
        if (this._am && !this._updating) 
        {
            this._updateListener = new jsb.EventListenerAssetsManager(this._am, this.updateCb.bind(this));
            cc.eventManager.addListener(this._updateListener, 1);

            this._am.update();
            this._updating = true;
        }
    }

 checkCb(event) 
    {
        cc.log('Code: ' + event.getEventCode());
        let skipUpdate:boolean=false;
        let startUpdate:boolean=false;
        switch (event.getEventCode())
        {
            case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST://获取本地manifest失败
                console.log("No local manifest file found, hot update skipped.");
                skipUpdate=true;
                break;
            case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST://下载远程manifest失败
            case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST://解析远程manifest失败
                console.log("Fail to download manifest file, hot update skipped.");
                skipUpdate=true;
                break;
            case jsb.EventAssetsManager.ALREADY_UP_TO_DATE://已经是最新版本
                console.log("Already up to date with the latest remote version,hot update skipped");
                skipUpdate=true;
                break;
            case jsb.EventAssetsManager.NEW_VERSION_FOUND://发现新版本
                console.log('New version found, please try to update.');
                startUpdate=true;
                break;
            default:
                return;
        }
        
        cc.eventManager.removeListener(this._checkListener);
        this._checkListener = null;
        this._updating = false;

        if (skipUpdate) 
        {
            //发送跳过更新消息
            MsgSystem.GetInstance().PostMsg(MsgDefine.hotUpdate_skip);
        }

        if (startUpdate) 
        {
            //发送开始更新消息
            MsgSystem.GetInstance().PostMsg(MsgDefine.hotUpdate_start);
            //开始更新
            this.StartUpdate();
        }
        
    }

如果检查到有更新 就开始进行更新

StartUpdate()
    {
        //开始更新
        if (this._am && !this._updating) 
        {
            this._updateListener = new jsb.EventListenerAssetsManager(this._am, this.updateCb.bind(this));
            cc.eventManager.addListener(this._updateListener, 1);

            this._am.update();
            this._updating = true;
        }
    }

更新过程中的监听

updateCb(event)
    {
        var finish = false;
        var failed = false;
        switch (event.getEventCode()) {
            // case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST://未找到本地 manifest
            //     console.log('No local manifest file found, hot update skipped.');
            //     failed = true;
            //     break;
            case jsb.EventAssetsManager.UPDATE_PROGRESSION://更新下载进度
                //this.panel.byteProgress.progress = event.getPercent();
                //this.panel.fileProgress.progress = event.getPercentByFile();
                //this.panel.fileLabel.string = event.getDownloadedFiles() + ' / ' + event.getTotalFiles();
                //this.panel.byteLabel.string = event.getDownloadedBytes() + ' / ' + event.getTotalBytes();
                MsgSystem.GetInstance().PostMsg(MsgDefine.hotUpdate_percent,{percent:event.getPercent()});
                console.log(event.getPercent());                
                // var msg = event.getMessage();
                // if (msg) {
                //     //this.panel.info.string = 'Updated file: ' + msg;
                //     // cc.log(event.getPercent()/100 + '% : ' + msg);
                // }
                break;
            // case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST://下载远程服务器上的manifest失败
            // case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST://解析远程服务器上的manifest失败
            //     console.log('Fail to download manifest file, hot update skipped.');
            //     failed = true;
            //     break;
            // case jsb.EventAssetsManager.ALREADY_UP_TO_DATE://已经升级到最近版本
            //     console.log('Already up to date with the latest remote version.');
            //     failed = true;
            //     break;
            case jsb.EventAssetsManager.UPDATE_FINISHED://更新完成
                console.log('Update finished. ' + event.getMessage());
                finish = true;
                break;
            case jsb.EventAssetsManager.UPDATE_FAILED://更新失败
                console.log('Update failed. ' + event.getMessage());
                this._updating = false;
                failed=true;
                break;
            case jsb.EventAssetsManager.ERROR_UPDATING://下载失败
                console.log('Asset update error: ' + event.getAssetId() + ', ' + event.getMessage());
                failed=true;
                break;
            case jsb.EventAssetsManager.ERROR_DECOMPRESS://解压失败
                console.log(event.getMessage());
                failed=true;
                break;
            default:
                break;
        }

        //如果更新失败
        if (failed) {
            cc.eventManager.removeListener(this._updateListener);
            this._updateListener = null;
            this._updating = false;

            //发送更新失败消息
            MsgSystem.GetInstance().PostMsg(MsgDefine.hotUpdate_faild);

        }

        //如果更新完成,需要重启
        if (finish) {
            //将下载的新的project.mainfest
            let filestring = jsb.fileUtils.getStringFromFile(this._storagePath + "/project.manifest");
            //转换成json
            let obj = JSON.parse(filestring);
            //修改3个远程url
            obj.packageUrl = "";
            obj.remoteManifestUrl = "/project.manifest";
            obj.remoteVersionUrl = "/version.manifest";
            let afterString = JSON.stringify(obj);
            //console.log(afterString);
            jsb.fileUtils.writeStringToFile(afterString, this._storagePath + "/originProject.manifest")

            cc.eventManager.removeListener(this._updateListener);
            this._updateListener = null;
            // Prepend the manifest's search path
            var searchPaths = jsb.fileUtils.getSearchPaths();
            var newPaths = this._am.getLocalManifest().getSearchPaths();
            console.log(JSON.stringify(newPaths));
            Array.prototype.unshift(searchPaths, newPaths);
            // This value will be retrieved and appended to the default search path during game startup,
            // please refer to samples/js-tests/main.js for detailed usage.
            // !!! Re-add the search paths in main.js is very important, otherwise, new scripts won't take effect.
            cc.sys.localStorage.setItem('HotUpdateSearchPaths', JSON.stringify(searchPaths));
            jsb.fileUtils.setSearchPaths(searchPaths);
   
            cc.audioEngine.stopAll();
            //cc.game.restart();
            //发送更新完成的消息
            MsgSystem.GetInstance().PostMsg(MsgDefine.hotUpdate_finish);
            //setTimeout(function(){cc.game.restart();},3000);
        }
    }

在检测到更新完成以后 我写了以下代码 重新生成originProject.manifest,来确保以后都可以正常的热更新,去掉了固定的url,对应到开头处理本地manifest的地方

if (finish) {
                //将下载的新的project.mainfest
                let filestring = jsb.fileUtils.getStringFromFile(this._storagePath + "/project.manifest");
                //转换成json
                let obj = JSON.parse(filestring);
                //修改3个远程url
                obj.packageUrl = "";
                obj.remoteManifestUrl = "/project.manifest";
                obj.remoteVersionUrl = "/version.manifest";
                let afterString = JSON.stringify(obj);
                //console.log(afterString);
                jsb.fileUtils.writeStringToFile(afterString, this._storagePath + "/originProject.manifest")

写了很多 请高手帮忙看看,特别是我生成文件目录的地方是不是错了

补充说明下,测试的时候 我是打包成 mac 版本的.app文件进行测试的,服务器就是本机

自己来划重点 可能会出问题的地方

设置热更新的目录
this._storagePath = ((jsb.fileUtils ? jsb.fileUtils.getWritablePath() : ‘/’) + ‘GHGame’);

加工manifest的地方

    //读取原始Manifest
    let filestring;
    if (jsb.fileUtils.isFileExist(this._storagePath + "/originProject.manifest")) 
    {
        filestring = jsb.fileUtils.getStringFromFile(this._storagePath + "/originProject.manifest");
    }
    else 
    {
        filestring = jsb.fileUtils.getStringFromFile(this.originManifestUrl);
    }
    console.log(filestring);
    
    //转换成json
    let obj = JSON.parse(filestring);
    //拼接远程3个远程url
    //let url=GameRuntimeData.GetInstance().url;
    let url = "http://127.0.0.1/GHGame/hotUpdateServer/GHGame";
    obj.packageUrl = url + obj.packageUrl;
    obj.remoteManifestUrl = url + obj.remoteManifestUrl;
    obj.remoteVersionUrl = url + obj.remoteVersionUrl;
    this._manifestObj = obj;
    //保存成本地文件
    let afterString = JSON.stringify(obj);
    console.log(afterString);
    jsb.fileUtils.writeStringToFile(afterString, this._storagePath + "/project.manifest")
    this._am.loadLocalManifest(this._storagePath + "/project.manifest");

更新完成后 重新保存manifest的地方

if (finish) {
            //将下载的新的project.mainfest
            let filestring = jsb.fileUtils.getStringFromFile(this._storagePath + "/project.manifest");
            //转换成json
            let obj = JSON.parse(filestring);
            //修改3个远程url
            obj.packageUrl = "";
            obj.remoteManifestUrl = "/project.manifest";
            obj.remoteVersionUrl = "/version.manifest";
            let afterString = JSON.stringify(obj);
            //console.log(afterString);
            jsb.fileUtils.writeStringToFile(afterString, this._storagePath + "/originProject.manifest")
            cc.eventManager.removeListener(this._updateListener);
            this._updateListener = null;
            // Prepend the manifest's search path
            var searchPaths = jsb.fileUtils.getSearchPaths();
            var newPaths = this._am.getLocalManifest().getSearchPaths();
            console.log(JSON.stringify(newPaths));
            Array.prototype.unshift(searchPaths, newPaths);
            // This value will be retrieved and appended to the default search path during game startup,
            // please refer to samples/js-tests/main.js for detailed usage.
            // !!! Re-add the search paths in main.js is very important, otherwise, new scripts won't take effect.
            cc.sys.localStorage.setItem('HotUpdateSearchPaths', JSON.stringify(searchPaths));
            jsb.fileUtils.setSearchPaths(searchPaths);

我自己感觉是这3个地方 不知道哪里出问题了

自己顶一下

@panda 求救命

你这个功能实现了吗 可以加下我扣969268210

后来发现原因了 可以看下我的另外一个帖子
http://forum.cocos.com/t/manifest/62466

代码在c++层 有个判断目录是否存在的处理,如果存在就清空掉 好像是这样的

    //读取原始Manifest
    let filestring;
    if (jsb.fileUtils.isFileExist(this._storagePath + "/originProject.manifest")) 
    {
        filestring = jsb.fileUtils.getStringFromFile(this._storagePath + "/originProject.manifest");
    }

这里不用判断缓存的project.manifest是否存在,直接使用本地的project.manifest(this.originManifestUrl)。

缓存目录下的project.manifest的版本号是最新的,跟远程版本号对比一致时会删除缓存目录,也是作为大版本更新需删除缓存目录的判断。
使用工程下assets目录下的project.manifest后,引擎会再一次对比缓存目录下的project.manifest。