大家在使用Cococ Creator提供的热更新 assetsManagers ,做md5校验的时候,一定会遇到卡顿的问题。
备注:文末有完整实现源码
原因是 Cococ Creator 官方提供的热更新校验回调是在ui线程进行,如下代码所示:
assetsManagers.setVerifyCallback(function (path, asset)) { let compressed = asset.compressed; let expectedMD5 = asset.md5; let relativePath = asset.path; let size = asset.size; if (compressed) { cc.log(`Verification passed : ${relativePath}`); .... return true; } else { cc.log(`Verification passed : ${relativePath} ( ${expectedMD5} )`); .... return true; } });
为了解决这个问题,需要对引擎热更新部分稍加改造。
下面以android 环境为例进行详细说明。
第一步:增加 md5库, 放在 cocos2d-x/extensions/assets-manager文件夹下 。
第二步,改造AssetsManagerEx.h 和 AssetsManagerEx.cpp 增加默认校验方法
在AssetsManagerEx.h 增加函数声明:
private: virtual bool onVerifyDefault(const std::string storagePath, Manifest::Asset asset);
在AssetsManagerEx.cpp 实现函数:
bool AssetsManagerEx::onVerifyDefault(const std::string storagePath,Manifest::Asset asset) { //cocos2d::log("onVerifyDefault 0"); Data data = cocos2d::FileUtils::getInstance()->getDataFromFile(storagePath); if (data.isNull() ||(data.getSize()== 0 )){ CCLOG("onVerifyDefault 1"); return false; } std::string result = md5(data.getBytes(), data.getSize()); //cocos2d::log("onVerifyDefault:%s - assetmd5:%s-resultmd5:%s",storagePath.c_str(),asset.md5.c_str(),result.c_str()); if (memcmp(result.c_str(), asset.md5.c_str(), result.length()) == 0){ CCLOG("onVerifyDefault 2"); return true; } // cocos2d::log("onVerifyDefault 3"); return false; }
在 AssetsManagerEx.cpp 的 onSuccess方法中,做md5比较部分改造。
当_verifyCallback 为nullptr 时,采用默认 onVerifyDefault 进行校验,如下:
void AssetsManagerEx::onSuccess(const std::string &/*srcUrl*/, const std::string &storagePath, const std::string &customId) { if (customId == VERSION_ID) { _updateState = State::VERSION_LOADED; parseVersion(); } else if (customId == MANIFEST_ID) { _updateState = State::MANIFEST_LOADED; parseManifest(); } else { if (_downloadingTask.find(customId) != _downloadingTask.end()) { _downloadingTask.erase(customId); } bool ok = true; auto &assets = _remoteManifest->getAssets(); auto assetIt = assets.find(customId); if (assetIt != assets.end()) { Manifest::Asset asset = assetIt->second; if (_verifyCallback != nullptr) { ok = _verifyCallback(storagePath, asset); } else{ ok =onVerifyDefault( storagePath,asset); } } if (ok) { bool compressed = assetIt != assets.end() ? assetIt->second.compressed : false; if (compressed) { decompressDownloadedZip(customId, storagePath); } else { fileSuccess(customId, storagePath); } } else { fileError(customId, "Asset file verification failed after downloaded"); } } }
第三步,修改 Android.mk,增加MD5.cpp 文件的编译
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := cocos_extension_static LOCAL_MODULE_FILENAME := libextension ifeq ($(USE_ARM_MODE),1) LOCAL_ARM_MODE := arm endif LOCAL_SRC_FILES := \ assets-manager/MD5.cpp \ assets-manager/Manifest.cpp \ assets-manager/AssetsManagerEx.cpp \ assets-manager/CCEventAssetsManagerEx.cpp \ assets-manager/CCAsyncTaskPool.cpp \ LOCAL_CXXFLAGS += -fexceptions LOCAL_C_INCLUDES := $(LOCAL_PATH)/. \ $(LOCAL_PATH)/.. \ $(LOCAL_PATH)/../cocos \ $(LOCAL_PATH)/../cocos/platform \ $(LOCAL_PATH)/../external/sources LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/. \ $(LOCAL_PATH)/.. include $(BUILD_STATIC_LIBRARY)
第四步,删除上层校验回调实现,默认采用c++新增的校验方法。
即:js 或ts 代码中不再调用 assetsManagers.setVerifyCallback 方法。
以上就是 Cocos Creator 热更新资源 md5值比较的调整,此方案可有效解决渲染线程卡顿问题。
下面是完整源码
MD5.h源码
#ifndef _LUA_MD5_H__ #define _LUA_MD5_H__ #include <stdio.h> #include <string> using namespace std; class MD5 { public: typedef unsigned int size_type; // must be 32bit MD5(); MD5(const std::string& text); void update(const unsigned char *buf, size_type length); void update(const char *buf, size_type length); MD5& finalize(); std::string hexdigest(bool bUpper = false) const; friend std::ostream& operator<<(std::ostream&, MD5 md5); private: void init(); typedef unsigned char uint1; // 8bit typedef unsigned int uint4; // 32bit enum { blocksize = 64 }; // VC6 won't eat a const static int here void transform(const uint1 block[blocksize]); static void decode(uint4 output[], const uint1 input[], size_type len); static void encode(uint1 output[], const uint4 input[], size_type len); bool finalized; uint1 buffer[blocksize]; uint4 count[2]; uint4 state[4]; uint1 digest[16]; static inline uint4 F(uint4 x, uint4 y, uint4 z); static inline uint4 G(uint4 x, uint4 y, uint4 z); static inline uint4 H(uint4 x, uint4 y, uint4 z); static inline uint4 I(uint4 x, uint4 y, uint4 z); static inline uint4 rotate_left(uint4 x, int n); static inline void FF(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); static inline void GG(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); static inline void HH(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); static inline void II(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); }; extern std::string md5(const std::string str); extern std::string md5(const wchar_t* pwstr); extern std::string md5(const unsigned char *buf, const unsigned int length); #endif