問題復現步驟:
一、在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); } } }