2.0.5热更新restart()后加载主场景报错

平台ios:
概率必现。
用一个update.fire场景挂载update脚本,启动场景也设为update.fire。假如第一次进去更新成功后调用cc.game.restart,此时游戏热重启又来到update场景,这时候assetManager判断已是最新版本。于是调用cc.loader.loadScene加载主场景main.fire。这时候就会报

ERROR: TypeError: split[1].split is not a function. (In ‘split[1].split("&")’, ‘split[1].split’ is undefined), location: src/cocos2d-jsb.js:21202:37
_parseUrlParam@src/cocos2d-jsb.js:21202:37
createItem@src/cocos2d-jsb.js:21216:35
append@src/cocos2d-jsb.js:21363:30
load@src/cocos2d-jsb.js:20223:21
loadAsset@src/cocos2d-jsb.js:24773:22
_loadSceneByUuid@src/cocos2d-jsb.js:6964:36
loadScene@src/cocos2d-jsb.js:6934:34

这时候强杀进程退出游戏,再进游戏就没问题,并且更新也成功了。
代码如下:

import { FileUtils } from "../common/native/fileutils";
import { AssetMgr } from "../logic/core/controller/assetmgr";
import { SceneMgr, SCENE_NAME } from "../common/ui/scene_mgr";
import { VersionCmpFunc } from "../const";

const verify_func = (path:string, asset) => {
    // When asset is compressed, we don't need to check its md5, because zip file have been deleted.
    let compressed:boolean = asset.compressed;
    // Retrieve the correct md5 value.
    let expectedMD5:string = asset.md5;
    // asset.path is relative path and path is absolute.
    let relativePath:string = asset.path;
    // The size of asset file, but this value could be absent.
    let size:number = asset.size;
    if (compressed) 
    {
        return true;
    }
    else 
    {
        return true;
    }
}

const LOCAL_MANIFEST = "project.manifest";  //放在build根目录。此目录默认在searchPath中

const {ccclass, property} = cc._decorator;
@ccclass
export class HotUpdate extends cc.Component {
    @property(cc.Label)
    txt_version: cc.Label = null;

    @property(cc.Label)
    txt_info: cc.Label = null;

    @property(cc.Label)
    txt_progress: cc.Label = null;

    @property(cc.Node)
    progressbar: cc.Node = null;

    private _am:jsb.AssetsManager;
    private _fileUtils:FileUtils;

    onLoad() 
    {
        // Hot update is only available in Native build
        if (!cc.sys.isNative)
        {
            cc.log("is not native platform, startGame directly");
            this.startGame();
            return;
        }

        //版本文件不存在,直接进入游戏
        this._fileUtils = FileUtils.getInst();
        const localManifestExist = this._fileUtils.isFileExist(LOCAL_MANIFEST);
        if(!localManifestExist)
        {
            cc.log("localManifest file not exist, startGame directly");
            this.startGame();
            return;
        }

        //加载本地版本文件
        const storagePath = this._fileUtils.getWritablePath() + 'pianotap-remote-asset';
        cc.log('Storage path for remote asset : ' + storagePath);
        this._am = new jsb.AssetsManager("", storagePath, VersionCmpFunc);
        this._am.setVerifyCallback(verify_func);

        const manifestStr = this._fileUtils.getStringFromFile(LOCAL_MANIFEST);
        const manifest = new jsb.Manifest(manifestStr, storagePath);
        this._am.loadLocalManifest(manifest, storagePath);


        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);
            cc.log("Max concurrent tasks count have been limited to 2");
        }

        this.checkUpdate();
    }

    onDestroy()
    {
        if(this._am)
        {
            this._am.setEventCallback(null);
            this._am = null;
        }
    }

    private updateCb(event) 
    {
        cc.log("hotupdate updateCb", event.getEventCode())
        let isUpdateSuccess = false;
        let failed = false;
        switch (event.getEventCode())
        {
            case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
                cc.log('No local manifest file found, hot update skipped.');
                failed = true;
                break;
            case jsb.EventAssetsManager.UPDATE_PROGRESSION:
                const bytePercent = event.getPercent();
                const msg = event.getMessage();
                if(msg)
                {
                    this.txt_info.string = `updating...`;
                }
                this.txt_progress.string = `loading:${(Math.min(1, bytePercent) * 100).toFixed(2)}%`;
                this.progressbar.width = bytePercent * this.node.width;
                break;
            case jsb.EventAssetsManager.ASSET_UPDATED:
                cc.log("ASSET_UPDATED=>", event.getAssetId())
                this.txt_info.string = `asset updated ${event.getAssetId()}`;
                break;
            case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
            case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
                cc.log('Fail to download manifest file, hot update skipped.');
                failed = true;
                break;
            case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
                cc.log('Already up to date with the latest remote version.');
                failed = true;
                break;
            case jsb.EventAssetsManager.NEW_VERSION_FOUND:
                cc.log('New version found');
                this.txt_info.string = "new version found";
                this.txt_progress.node.active = true;
                this.progressbar.width = 0;
                break;
            case jsb.EventAssetsManager.UPDATE_FINISHED:
                cc.log('Update finished. ' + event.getMessage());
                isUpdateSuccess = true;
                break;
            case jsb.EventAssetsManager.UPDATE_FAILED:
                cc.log('Retry failed Assets...');
                this.txt_info.string = `update failed, ${event.getMessage()}`;
                this._am.downloadFailedAssets();
                break;
            case jsb.EventAssetsManager.ERROR_UPDATING:
                cc.log("Asset update error:", event.getAssetId(), event.getMessage());
                this.txt_info.string = `asset update error: ${event.getAssetId()}, ${event.getMessage()}`;
                break;
            case jsb.EventAssetsManager.ERROR_DECOMPRESS:
                cc.log("ERROR_DECOMPRESS", event.getMessage());
                break;
            default:
                break;
        }

        //更新成功重启游戏
        if(isUpdateSuccess) 
        {
            this._am.setEventCallback(null);

            // Prepend the manifest's search path
            let searchPaths = this._fileUtils.getSearchPaths();
            let newPaths = this._am.getLocalManifest().getSearchPaths();
            Array.prototype.unshift.apply(searchPaths, newPaths);
            searchPaths.forEach((element, i) => {
                cc.log(element, i);
            });
            this._fileUtils.setSearchPaths(searchPaths);

            //保存searchPaths到本地存储供main.js读取。
            const searchPathStr = JSON.stringify(searchPaths);
            cc.sys.localStorage.setItem('HotUpdateSearchPaths', searchPathStr);
            cc.log("hotupdate setSearchPaths->", searchPathStr);

            //重启游戏
            cc.audioEngine.stopAll();
            cc.game.restart();
        }
        else if(failed)
        {
            this.startGame();
        }
    }

    private checkUpdate() 
    {
        if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
            cc.warn("!!!am.getState() === jsb.AssetsManager.State.UNINITED");
            return;
        }
        this._am.setEventCallback(this.updateCb.bind(this));
        this._am.update();
    }

    private startGame()
    {
        if(cc.sys.isNative)
        {
            AssetMgr.getInst().checkRemoteAssetVersion();
        }
        SceneMgr.getInst().loadScene(SCENE_NAME.main);
    }
}

@panda

有点坑的感觉

有遇到同样问题的同学吗

有遇到同样的问题的童鞋可以分享一下解决方法

我们这边测试工程没遇到这种情况,还是要排查你自己的逻辑实现,跟踪一下报错位置。

好的,我再观查