Cocos 更新時反覆殺進程,致使差別更新失效的Bug

Cocos 更新時反覆殺進程時,差別更新失效的問題:

問題復現步驟:
一、在project.manifest.temp 文件下載成功後,下載Assets資源的時候殺掉進程
二、重啓遊戲,繼續更新時會使用上次下載成功的project.manifest.temp文件,這個時候由於沒有將文件下載狀態保存,而更新的時候又判斷沒有下再成功就去下載,就致使將全部文件都下載了。this

下載流程分析

一、 project.manifest.temp 文件下載成功以前若是kill進程,下次進入就會刪除這個文件,從新下載;PS:調用update
接口開始更新時kill進程,再次啓動都會引發project.manifest.temp的從新下載,這也是致使bug的緣由之一。spa

// 這個方法會在更新以前調用
void AssetsManagerEx::initManifests(const std::string& manifestUrl)
{
    _inited = true;
    // Init and load local manifest
    _localManifest = new (std::nothrow) Manifest();
    if (_localManifest)
    {
        loadLocalManifest(manifestUrl);

        // Init and load temporary manifest
        _tempManifest = new (std::nothrow) Manifest();
        if (_tempManifest)
        {
            _tempManifest->parse(_tempManifestPath);
            // 這裏判斷若是文件不是完整的,而且存在就刪除它;
            if (!_tempManifest->isLoaded() && _fileUtils->isFileExist(_tempManifestPath))
                _fileUtils->removeFile(_tempManifestPath);
        }
        else
        {
            _inited = false;
        }

        // Init remote manifest for future usage
        _remoteManifest = new (std::nothrow) Manifest();
        if (!_remoteManifest)
        {
            _inited = false;
        }
    }
    else
    {
        _inited = false;
    }

    if (!_inited)
    {
        CC_SAFE_DELETE(_localManifest);
        CC_SAFE_DELETE(_tempManifest);
        CC_SAFE_DELETE(_remoteManifest);
    }
}

二、開始下載根據project.manifest.temp 臨時文件是否完整存在來決定是恢復以前的下載狀態,仍是根據從新下載回來的manifest文件與本地文件對比差別度,決定下載那些。rest

void AssetsManagerEx::startUpdate()
{
    if (_updateState != State::NEED_UPDATE)
        return;

    _updateState = State::UPDATING;
    // Clean up before update
    _failedUnits.clear();
    _downloadUnits.clear();
    _compressedFiles.clear();
    _totalWaitToDownload = _totalToDownload = 0;
    _percent = _percentByFile = _sizeCollected = _totalSize = 0;
    _downloadedSize.clear();
    _totalEnabled = false;

    // Temporary manifest exists, resuming previous download
    if (_tempManifest->isLoaded() && _tempManifest->versionEquals(_remoteManifest))
    {
        // 文件是完整的,直接從文件恢復下載,而且從文件中讀取下載狀態,來判斷是否須要下載
        // 更新完成以前kill進程和每次啓動程序下載project.manifest.temp文件都會致使下載狀態被清空
        // 恢復下載是根據manifest臨時文件中保存的下載狀態來決定,詳細見3
        _tempManifest->genResumeAssetsList(&_downloadUnits);
        _totalWaitToDownload = _totalToDownload = (int)_downloadUnits.size();
        this->batchDownload();

        std::string msg = StringUtils::format("Resuming from previous unfinished update, %d files remains to be finished.", _totalToDownload);
        CCLOG(msg);
        // this time , the remoteManifest(has no DownloadState) file overwrite tempManifest file. when we kill process, and restart, it will uses error file.  
    // 爲了不被從新下載的manifest文件覆蓋掉下載狀態,咱們這裏須要將當前狀態再次寫入文件備份
        _tempManifest->saveToFile(_tempManifestPath);
        dispatchUpdateEvent(EventAssetsManagerEx::EventCode::UPDATE_PROGRESSION, "", msg);
    }
    // Check difference
    else
    {
        // Temporary manifest not exists or out of date,
        // it will be used to register the download states of each asset,
        // in this case, it equals remote manifest.
        _tempManifest->release();
        _tempManifest = _remoteManifest;

        std::unordered_map<std::string, Manifest::AssetDiff> diff_map = _localManifest->genDiff(_remoteManifest);

        std::string log = StringUtils::format("AssetsManagerEx : Diff file size %d", diff_map.size());
        CCLOG(log);

        if (diff_map.size() == 0)
        {
            CCLOG("AssetsManagerEx updateSucceed");
            updateSucceed();
        }
        else
        {
            // Generate download units for all assets that need to be updated or added
            std::string packageUrl = _remoteManifest->getPackageUrl();
            for (auto it = diff_map.begin(); it != diff_map.end(); ++it)
            {
                Manifest::AssetDiff diff = it->second;

                if (diff.type == Manifest::DiffType::DELETED)
                {
                    CCLOG("AssetsManagerEx DELETED " + diff.asset.path);
                    _fileUtils->removeFile(_storagePath + diff.asset.path);
                }
                else
                {
                    std::string path = diff.asset.path;
                    CCLOG("AssetsManagerEx " + diff.asset.path + " type "+ diff.type);
                    // Create path
                    _fileUtils->createDirectory(basename(_storagePath + path));

                    DownloadUnit unit;
                    unit.customId = it->first;
                    unit.srcUrl = packageUrl + path;
                    unit.storagePath = _storagePath + path;
                    _downloadUnits.emplace(unit.customId, unit);
                }
            }
            // Set other assets' downloadState to SUCCESSED
            auto &assets = _remoteManifest->getAssets();
            for (auto it = assets.cbegin(); it != assets.cend(); ++it)
            {
                const std::string &key = it->first;
                auto diffIt = diff_map.find(key);
                if (diffIt == diff_map.end())
                {
                    // 根據文件對比,將不須要下載的文件的狀態設置爲下載成功
                    _tempManifest->setAssetDownloadState(key, Manifest::DownloadState::SUCCESSED);
                }
            }
            _totalWaitToDownload = _totalToDownload = (int)_downloadUnits.size();
            this->batchDownload();

            std::string msg = StringUtils::format("Start to update %d files from remote package.", _totalToDownload);           
            // 爲了不進程在更新完成以前被kill致使下載狀態丟失,這裏先保存文件,備份一次
            _tempManifest->saveToFile(_tempManifestPath);
            dispatchUpdateEvent(EventAssetsManagerEx::EventCode::UPDATE_PROGRESSION, "", msg);
        }
    }
}

三、恢復下載是,須要根據以前保存在臨時文件中的下載狀態來決定下載那些文件code

void Manifest::genResumeAssetsList(DownloadUnits *units) const
{
    for (auto it = _assets.begin(); it != _assets.end(); ++it)
    {
        Asset asset = it->second;

        if (asset.downloadState != DownloadState::SUCCESSED)
        {
            DownloadUnit unit;
            unit.customId = it->first;
            unit.srcUrl = _packageUrl + asset.path;
            unit.storagePath = _manifestRoot + asset.path;
            units->emplace(unit.customId, unit);
        }
    }
}
相關文章
相關標籤/搜索