五星之光地图包下载zip里把哪个文件放到m...

cocos2d-x android 直接加载下载到sd的zip里的资源文件
最近公司要做的一个cocos-x项目,这个项目用的是2.2.6版的cocos-x c++ 版,cocos比较老的版本。由于打包出来的apk超过了300M,而且资源无法热更新。面临这两个问题。我们讨论了一下,如何尽快的把包改到50m以内和在线更新新主题,对此研究了一下cocos的底层。了解到cocos可以通过
CCFileUtils::sharedFileUtils()-&addSearchPath(路径);
这样的方法来加载sd 里的资源,然后我们做了第一版。资源的加载方式:
1.将资源下载到sd对应的目录中。结合xutils 下载到对应的sd目录中。
HttpUtils http = new HttpUtils();
http.configRequestThreadPoolSize(MAXDOWNLOADTHREAD);
HttpHandler handler = http.download(downloadInfo.getDownloadUrl(), downloadInfo.getFileSavePath(), downloadInfo.isAutoResume(), downloadInfo.isAutoRename(), managerCallBack);
2.然后解压到对应的目录。
* 文件解决
* @param file
要解压的 zip文件
* @param savePath
解压到的路径
private Void doUnZip(String filePath, String savePath, UnZipListener unZipListener) {
File file = new File(filePath);
// Debug.printlili(&SDCardManger doUnZip&);
String unzipfile = file.getAbsolutePath(); // 解压缩的文件名包含路径
// File olddirec = // 解压缩的文件路径(为了获取路径)
// 保存的文件夹
String parent = saveP
ZipInputStream zin = new ZipInputStream(new FileInputStream(unzipfile));
// 创建文件夹
while ((entry = zin.getNextEntry()) != null) {
if (entry.isDirectory()) {
File directory = new File(parent, entry.getName());
if (!directory.exists())
if (!directory.mkdirs())
zin.closeEntry();
if (!entry.isDirectory()) {
File myFile = new File(entry.getName());
// 输出路径
String ofile =
File fo = new File(ofile);
if (!fo.exists()) {
fo.mkdir();
String fileSavePath = ofile + myFile.getPath();
// Debug.printlili(&unzip path:& + fileSavePath);
FileOutputStream fout = new FileOutputStream(ofile + myFile.getPath());
DataOutputStream dout = new DataOutputStream(fout);
byte[] b = new byte[1024];
int len = 0;
while ((len = zin.read(b)) != -1) {
dout.write(b, 0, len);
dout.close();
fout.close();
zin.closeEntry();
if (unZipListener != null) {
unZipListener.onZip(myFile, fileSavePath);
// file.delete();
} catch (IOException e) {
e.printStackTrace();
exception =
3.添加路径到 到 CCFileUtils-&SearchPath。通过jni 调用一下
public static native void addSearchPath(String path);
void _com_xxx_base_BaseGameActivity_addSearchPath(JNIEnv*
env, jobject thiz,jstring path)
const char *char_path = (env)-&GetStringUTFChars(path, NULL);
CCFileUtils::sharedFileUtils()-&addSearchPath(char_path);
经过时间的考验,这样的方式有不好毛病。1.解压到sd大的资源大大占用用户的sd卡。2.一些资源会被当做缓存被一些管理软件清理。3.解压过程会占用线程卡顿。
对此出了第二版 直接加载下载好的zip包里的资源。第二版的是在第一版的基础上 修改的。继续研究cocos的资源加载方式,翻阅一下cocos-x ,知道了cocos是如何通过一个简单的名字像xxxxbg.png 得到对应的图片资源的。
1.获得xxxxbg.png的fullpath。这个fullpath 有两种可能一种是apk中的assets中的绝对路径,一种是sd中的绝对路径。fullpath = searchpath + orderpath + filename
查阅CCFileUtils.cpp
-&std::string CCFileUtils::fullPathForFilename(const char* pszFileName)
-&std::string CCFileUtils::getPathForFilename(const std::string& filename, const std::string& resolutionDirectory, const std::string& searchPath)
-&std::string CCFileUtils::getFullPathForDirectoryAndFilename(const std::string& strDirectory, const std::string& strFilename)
-&bool CCFileUtils::isFileExist(const std::string& strFilePath)
当进入到isFileExit知道了这个方法时 跳转到了CCFileUtilsAndroid.cpp
大概知道了fullPathForFilename这个方法如何工作的,大概意思是 searchpath数组 + orderpath数字 双层循环遍历一下 filename 的fullpath,如果存在这个文件就返回fullpath,进入下一步读取数据。isFileExist是关键的方法我们来看看这方法。
bool CCFileUtilsAndroid::isFileExist(const std::string& strFilePath) { //拼接好的fullpath 长度是否为0,等于0 这个文件就标记为不存当前的临时fullpath if (0 == strFilePath.length()) { } bool bFound = // Check whether file exists in apk. //如果fullpath是/开头说明这个路径是在assets中的 s_pZipFile指向apk assets if (strFilePath[0] != '/') { std::string strPath = strFileP if (strPath.find(m_strDefaultResRootPath) != 0) {// Didn't find &assets/& at the beginning of the path, adding it. strPath.insert(0, m_strDefaultResRootPath); } if (s_pZipFile-&fileExists(strPath)) { bFound = } } //否则就是sd卡的路径 简单的读取,指针不是空的就是存在咯 else { FILE *fp = fopen(strFilePath.c_str(), &r&); if(fp) { bFound = fclose(fp); } } return bF }
2.用fullpath 获得 图片或者声音,plist的unsigned char* 数据。
-&unsigned char* CCFileUtils::getFileData(const char* pszFileName, const char* pszMode, unsigned long * pSize)
-&unsigned char* CCFileUtilsAndroid::doGetFileData(const char* pszFileName, const char* pszMode, unsigned long * pSize, bool forAsync)
unsigned char* CCFileUtilsAndroid::doGetFileData(const char* pszFileName, const char* pszMode, unsigned long * pSize, bool forAsync)
unsigned char * pData = 0;
if ((! pszFileName) || (! pszMode) || 0 == strlen(pszFileName))
string fullPath = fullPathForFilename(pszFileName);
//如果是assets路径下的 加载异步或同步的数据
if (fullPath[0] != '/')
if (forAsync)
pData = s_pZipFile-&getFileData(fullPath.c_str(), pSize, s_pZipFile-&_dataThread);
pData = s_pZipFile-&getFileData(fullPath.c_str(), pSize);
//简单的打开file读取文件
// read rrom other path than user set it
//CCLOG(&GETTING FILE ABSOLUTE DATA: %s&, pszFileName);
FILE *fp = fopen(fullPath.c_str(), pszMode);
CC_BREAK_IF(!fp);
fseek(fp,0,SEEK_END);
size = ftell(fp);
fseek(fp,0,SEEK_SET);
pData = new unsigned char[size];
size = fread(pData,sizeof(unsigned char), size,fp);
fclose(fp);
if (pSize)
} while (0);
if (! pData)
std::string msg = &Get data from file(&;
msg.append(pszFileName).append(&) failed!&);
CCLOG(&%s&, msg.c_str());
既然知道安卓这边是如何读取数据的,接下来我们来思考一下如何对zip里的一个文件也可以检查 并返回fullpath 然后读取数据。
好比如下的路径
/storage/emulated/0/DonutABC/unitRes/game_22.zip里的/res/pub_element/L1U1/audio/pub_unit1_blue_audio.wav
1.获得fullpath。cocos中获得fullpath 就两种apk assets中的和sd中的,想想sd卡中的路径只需要添加searchpath就能找到对应资源,我们也可以把zip文件当做path添加到searchpath中,在 检测文件是否存在中。我们追加检测一下zip里的文件是否存在不就可以获得fullpath了吗。
获得如下的fullpath,用&#'结尾,区别他是sd卡中的searchpath,还是zip中的searchpath
searchpath =/storage/emulated/0/DonutABC/unitRes/game_22.zip#
orderpath + filename =/res/pub_element/L1U1/audio/pub_unit1_blue_audio.wav
fullpath = /storage/emulated/0/DonutABC/unitRes/game_22.zip#/res/pub_element/L1U1/audio/pub_unit1_blue_audio.wav
bool CCFileUtilsAndroid::isFileExist(const std::string& strFilePath)
if (0 == strFilePath.length())
bool bFound =
// Check whether file exists in apk.
if (strFilePath[0] != '/')
std::string strPath = strFileP
if (strPath.find(m_strDefaultResRootPath) != 0)
{// Didn't find &assets/& at the beginning of the path, adding it.
strPath.insert(0, m_strDefaultResRootPath);
if (s_pZipFile-&fileExists(strPath))
//看是否为#的路径 用zip方法里的方法检测文件的存在 zlib库检测一下文件是否存在
// /storage/emulated/0/DonutABC/unitRes/game_22.zip#/res/pub_element/L1U1/audio/pub_unit1_blue_audio.wav
std::string pszFileName = strFileP
std::string pszZipFilePath = &&;
size_t pos = strFilePath.find_last_of(&#&);
if (pos != std::string::npos)
//CCLOG(&isFileExist###########strFilePath:%s&, strFilePath.c_str());
// file_path = /storage/emulated/0/DonutABC/unitRes/game_22.zip
pszZipFilePath = strFilePath.substr(0, pos);
// file = res/pub_element/L1U1/audio/pub_unit1_blue_audio.wav
pszFileName = strFilePath.substr(pos+2);
//CCLOG(&isFileExist###########zip path:file_path:%s
file:%s&,pszZipFilePath.c_str(),pszFileName.c_str());
unzFile pFile = NULL;
CC_BREAK_IF(!pszZipFilePath.c_str() || !pszFileName.c_str());
CC_BREAK_IF(strlen(pszZipFilePath.c_str()) == 0);
pFile = unzOpen(pszZipFilePath.c_str());
int nRet = unzLocateFile(pFile, pszFileName.c_str(), 1);
//CCLOG(&isFileExist###########nRet:%d&,nRet);
if (UNZ_OK == nRet)
// CCLOG(&isFileExist###########UNZ_OK&);
if (pFile)
unzClose(pFile);
} while (0);
FILE *fp = fopen(strFilePath.c_str(), &r&);
fclose(fp);
2.获得资源数据。仔细分析CCFileUtils.cpp 会看到一个方法unsigned char* CCFileUtils::getFileDataFromZip(const char* pszZipFilePath, const char* pszFileName, unsigned long * pSize) 是的这个方法就是读取zip文件里的数据的。给力吧
我们 修改一下 doGetFileData 方法 读取zip数据 就好了 ^ ^!
unsigned char* CCFileUtilsAndroid::doGetFileData(const char* pszFileName, const char* pszMode, unsigned long * pSize, bool forAsync)
unsigned char * pData = 0;
if ((! pszFileName) || (! pszMode) || 0 == strlen(pszFileName))
string fullPath = fullPathForFilename(pszFileName);
if (fullPath[0] != '/')
if (forAsync)
pData = s_pZipFile-&getFileData(fullPath.c_str(), pSize, s_pZipFile-&_dataThread);
pData = s_pZipFile-&getFileData(fullPath.c_str(), pSize);
// CCLOG(&doGetFileData###########strFilePath:%s&, fullPath.c_str());
//看是否为#的路径
// /storage/emulated/0/DonutABC/unitRes/game_22.zip#/res/pub_element/L1U1/audio/pub_unit1_blue_audio.wav
std::string pszFileNameTemp = fullP
std::string pszZipFilePath = &&;
size_t pos = fullPath.find_last_of(&#&);
if (pos != std::string::npos)
// file_path = /storage/emulated/0/DonutABC/unitRes/game_22.zip
pszZipFilePath = fullPath.substr(0, pos);
// file = res/pub_element/L1U1/audio/pub_unit1_blue_audio.wav
pszFileNameTemp = fullPath.substr(pos+2);
//CCLOG(&doGetFileData path:file_path:%s
file:%s&,pszZipFilePath.c_str(),pszFileNameTemp.c_str());
pData = getFileDataFromZip(pszZipFilePath.c_str(),pszFileNameTemp.c_str(),pSize);
// read rrom other path than user set it
//CCLOG(&GETTING FILE ABSOLUTE DATA: %s&, pszFileName);
FILE *fp = fopen(fullPath.c_str(), pszMode);
CC_BREAK_IF(!fp);
fseek(fp,0,SEEK_END);
size = ftell(fp);
fseek(fp,0,SEEK_SET);
pData = new unsigned char[size];
size = fread(pData,sizeof(unsigned char), size,fp);
fclose(fp);
if (pSize)
} while (0);
//by sc Load
if (pData) {
pData = ResourcesDecode::sharedDecode()-&decodeData(pData, *pSize, pSize);
if (! pData)
std::string msg = &Get data from file(&;
msg.append(pszFileName).append(&) failed!&);
CCLOG(&%s&, msg.c_str());
接下来,我们只要将下载的zip路径添加到searchpath 就可以读取了数据啦!
安卓这边得到sd卡路径
public static String getSDcardDir() {
return Environment.getExternalStorageDirectory().getPath() + &/&;
searchpath = sd路径+下载路径+&#&
CCFileUtils::sharedFileUtils()-&addSearchPath(&/storage/emulated/0/DonutABC/unitRes/game_22.zip#&);
有同学就问了cocos里某张图片的路径如何填
当然是 zip里的路径啦。/res/pub_element/L1U1/audio/pub_unit1_blue_audio.wav
打开压缩包 就看见啦。
下载完整的修改&
(window.slotbydup=window.slotbydup || []).push({
id: '2467140',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467141',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467142',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467143',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467148',
container: s,
size: '1000,90',
display: 'inlay-fix'

参考资料

 

随机推荐