cocos2dx文件管理类是一个很重要的类,这里对这个类进行一下分析: CCFileUtils是文件管理类的基类,不同平台下android,ios,win32都有 继承于这个类的子类,如android class CC_DLL CCFileUtilsAndroid : public CCFileUtils 1、单例类: static CCFileUtils* sharedFileUtils() 而实现却在CCFileUtilsAndroid.cpp文件中:并且创建的是各个平台下的子类实例 CCFileUtils* CCFileUtils::sharedFileUtils() { if (s_sharedFileUtils == NULL) { s_sharedFileUtils = new CCFileUtilsAndroid(); s_sharedFileUtils->init(); //获取apk包的路径,这里是java端设置的。 std::string resourcePath = getApkPath(); // record the zip on the resource path // static ZipFile *s_pZipFile = NULL; // 因为android的很多资源是放在安装包里的assets文件里, //所以获取资源是需要从包里解压,这就用到了ZipFile类 s_pZipFile = new ZipFile(resourcePath,"assets/"); } return s_sharedFileUtils; } 2、初始化: bool CCFileUtilsAndroid::init() { m_strDefaultResRootPath = "assets/"; //默认的资源路径,默认是安装包 return CCFileUtils::init(); -->> 1 } 1 -->> bool CCFileUtils::init() { //m_searchPathArray -- 资源搜索路径数组 //m_searchResolutionsOrderArray -- 资源分辨率数组 m_searchPathArray.push_back(m_strDefaultResRootPath); m_searchResolutionsOrderArray.push_back(""); return true; }
在1中,我只是针对整体结构进行了分析,那么在2中,我将会对一些我们常用的函数进行分析。 //获取给定文件名的全路径 //下面这很长一段注释,通过举例子,像我们说明cocos2dx获取文件全路径的规则。 //这段我就不翻译了,直接通过代码来看。 /** Returns the fullpath for a given filename. First it will try to get a new filename from the "filenameLookup" dictionary. If a new filename can't be found on the dictionary,it will use the original filename. Then it will try to obtain the full path of the filename using the CCFileUtils search rules: resolutions,and search paths. The file search is based on the array element order of search paths and resolution directories. For instance: We set two elements("/mnt/sdcard/","internal_dir/") to search paths vector by setSearchPaths,and set three elements("resources-ipadhd/","resources-ipad/","resources-iphonehd") to resolutions vector by setSearchResolutionsOrder. The "internal_dir" is relative to "Resources/". If we have a file named 'sprite.png',the mapping in fileLookup dictionary contains `key: sprite.png -> value: sprite.pvr.gz`. Firstly,it will replace 'sprite.png' with 'sprite.pvr.gz',then searching the file sprite.pvr.gz as follows: /mnt/sdcard/resources-ipadhd/sprite.pvr.gz (if not found,search next) /mnt/sdcard/resources-ipad/sprite.pvr.gz (if not found,search next) /mnt/sdcard/resources-iphonehd/sprite.pvr.gz (if not found,search next) /mnt/sdcard/sprite.pvr.gz (if not found,search next) internal_dir/resources-ipadhd/sprite.pvr.gz (if not found,search next) internal_dir/resources-ipad/sprite.pvr.gz (if not found,search next) internal_dir/resources-iphonehd/sprite.pvr.gz (if not found,search next) internal_dir/sprite.pvr.gz (if not found,return "sprite.png") If the filename contains relative path like "gamescene/uilayer/sprite.png",and the mapping in fileLookup dictionary contains `key: gamescene/uilayer/sprite.png -> value: gamescene/uilayer/sprite.pvr.gz`. The file search order will be: /mnt/sdcard/gamescene/uilayer/resources-ipadhd/sprite.pvr.gz (if not found,search next) /mnt/sdcard/gamescene/uilayer/resources-ipad/sprite.pvr.gz (if not found,search next) /mnt/sdcard/gamescene/uilayer/resources-iphonehd/sprite.pvr.gz (if not found,search next) /mnt/sdcard/gamescene/uilayer/sprite.pvr.gz (if not found,search next) internal_dir/gamescene/uilayer/resources-ipadhd/sprite.pvr.gz (if not found,search next) internal_dir/gamescene/uilayer/resources-ipad/sprite.pvr.gz (if not found,search next) internal_dir/gamescene/uilayer/resources-iphonehd/sprite.pvr.gz (if not found,search next) internal_dir/gamescene/uilayer/sprite.pvr.gz (if not found,return "gamescene/uilayer/sprite.png") If the new file can't be found on the file system,it will return the parameter pszFileName directly. @since v2.1 */ virtual std::string fullPathForFilename(const char* pszFileName); -->> std::string CCFileUtils::fullPathForFilename(const char* pszFileName) { CCAssert(pszFileName != NULL,"CCFileUtils: Invalid path"); //判断是否是绝对路径,如果是绝对路径就直接返回。 /* //android下 判断依据就是是否以'/'开头或者以assets/开头。下面这个函数,注释的很清楚。 //你可以做个实验: //例: Get data from file(/second_bg.png) Failed! 我在创建精灵时传递/second_bg.png路径 bool CCFileUtilsAndroid::isAbsolutePath(const std::string& strPath) { // On Android,there are two situations for full path. // 1) Files in APK,e.g. assets/path/path/file.png // 2) Files not in APK,e.g. /data/data/org.cocos2dx.hellocpp/cache/path/path/file.png,or /sdcard/path/path/file.png. // So these two situations need to be checked on Android. if (strPath[0] == '/' || strPath.find(m_strDefaultResRootPath) == 0) { return true; } return false; } */ std::string strFileName = pszFileName; if (isAbsolutePath(pszFileName)) { //CCLOG("Return absolute path( %s ) directly.",pszFileName); return pszFileName; } // Already Cached ? //是否已经缓存,如果缓存过,直接返回 std::map<std::string,std::string>::iterator cacheIter = m_fullPathCache.find(pszFileName); if (cacheIter != m_fullPathCache.end()) { //CCLOG("Return full path from cache: %s",cacheIter->second.c_str()); return cacheIter->second; } /* std::string CCFileUtils::getNewFilename(const char* pszFileName) { const char* pszNewFileName = NULL; // in Lookup Filename dictionary ? //可以把这个m_pFilenameLookupDict(默认为NULL)字典理解为一种查找 //比如这个字典里存了一个"fish.png(key)" --> "big_fish.png(value)" //那么我们传入fish.png是,就会给我们转化为big_fish.png。如果没有,则返回我们传入的。 CCString* fileNameFound = m_pFilenameLookupDict ? (CCString*)m_pFilenameLookupDict->objectForKey(pszFileName) : NULL; if( NULL == fileNameFound || fileNameFound->length() == 0) { pszNewFileName = pszFileName; } else { pszNewFileName = fileNameFound->getCString(); //CCLOG("FOUND NEW FILE NAME: %s.",pszNewFileName); } return pszNewFileName; } */ // Get the new file name. std::string newFilename = getNewFilename(pszFileName); string fullpath = ""; //下面这一段很关键: //m_searchPathArray 前面介绍过搜索路径数组,需要我们手动设置。android的初始话会添加一个默认值为 //m_searchPathArray.push_back(m_strDefaultResRootPath)即,"assets/"。 /* m_searchResolutionsOrderArray 可以理解为分辨率搜索顺序,就按开头注释说明的那样 //m_searchPathArray We set two elements("/mnt/sdcard/",//m_searchResolutionsOrderArray and set three elements("resources-ipadhd/","resources-iphonehd") to resolutions vector by setSearchResolutionsOrder. //组合后的路径 /mnt/sdcard/resources-ipadhd/sprite.pvr.gz (if not found,search next) /mnt/sdcard/resources-ipad/sprite.pvr.gz (if not found,search next) /mnt/sdcard/resources-iphonehd/sprite.pvr.gz (if not found,search next) 总结:从这里可以看出,m_searchPathArray在前面的路径,会优先搜索,m_searchResolutionsOrderArray也一样。 */ for (std::vector<std::string>::iterator searchPathsIter = m_searchPathArray.begin(); searchPathsIter != m_searchPathArray.end(); ++searchPathsIter) { for (std::vector<std::string>::iterator resOrderIter = m_searchResolutionsOrderArray.begin(); resOrderIter != m_searchResolutionsOrderArray.end(); ++resOrderIter) { //CCLOG("\n\nSEARCHING: %s,%s,%s",newFilename.c_str(),resOrderIter->c_str(),searchPathsIter->c_str()); //下面我分析一下这个函数:-->> 2 fullpath = this->getPathForFilename(newFilename,*resOrderIter,*searchPathsIter); //这里会对找到的路径,进行缓存 if (fullpath.length() > 0) { // Using the filename passed in as key. m_fullPathCache.insert(std::pair<std::string,std::string>(pszFileName,fullpath)); //CCLOG("Returning path: %s",fullpath.c_str()); return fullpath; } } } //CCLOG("cocos2d: fullPathForFilename: No file found at %s. Possible missing file.",pszFileName); // The file wasn't found,return the file name passed in. return pszFileName; } --> 2 //filename -- 传入的文件名 //searchPath -- 搜索路径 //resolutionDirectory -- 资源分辨率路径 std::string CCFileUtils::getPathForFilename(const std::string& filename,const std::string& resolutionDirectory,const std::string& searchPath) { std::string file = filename; std::string file_path = ""; size_t pos = filename.find_last_of("/"); if (pos != std::string::npos) { file_path = filename.substr(0,pos+1); file = filename.substr(pos+1); } //如果传入的"gamescene/uilayer/sprite.png"是这样的路径,那么进行一定的处理, //处理成:path = searchPath + gamescene/uilayer/ + resourceDirectory file = sprite.png ///mnt/sdcard/ gamescene/uilayer/ resources-ipadhd/sprite.pvr.gz // searchPath + file_path + resourceDirectory std::string path = searchPath; path += file_path; path += resolutionDirectory; path = getFullPathForDirectoryAndFilename(path,file); //CCLOG("getPathForFilename,fullPath = %s",path.c_str()); return path; } -->> std::string CCFileUtils::getFullPathForDirectoryAndFilename(const std::string& strDirectory,const std::string& strFilename) { std::string ret = strDirectory+strFilename; //如果文件存在,就把文件的路径返回,这个路径可能是绝对路径,也可能是包里的路径 if (!isFileExist(ret)) { ret = ""; } return ret; } -->> //把上面合成的整个文件路径传进去,判断文件是否存在 bool CCFileUtilsAndroid::isFileExist(const std::string& strFilePath) { if (0 == strFilePath.length()) { return false; } bool bFound = false; // Check whether file exists in apk. //如果不是以'/'开头,就在android包里查找 if (strFilePath[0] != '/') { //如果不是以"assets/"开头,则插入 std::string strPath = strFilePath; 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 = true; } } else { //如果是绝对路径,看否打开成功,如果成功,则证明文件存在。 FILE *fp = fopen(strFilePath.c_str(),"r"); if(fp) { bFound = true; fclose(fp); } } return bFound; } 总结:这里需要知道一点,就是先加载搜索路径的路径,会优先搜索到。 比如热更新,我们只要把更新路径设置在前面即可。
在2中,我们分析了几个函数,在这一篇中我们继续分析其他一些函数。 1、 在2中,多次用到了m_searchPathArray(搜索路径),那这个搜索路径怎么来的呢? 我们可以通过setSearchPaths这个函数来设置搜索路径 void CCFileUtils::setSearchPaths(const std::vector<std::string>& searchPaths) { bool bExistDefaultRootPath = false; //先把以前的清空,包括缓存路径 m_fullPathCache.clear(); m_searchPathArray.clear(); //逐个加入到m_searchPathArray for (std::vector<std::string>::const_iterator iter = searchPaths.begin(); iter != searchPaths.end(); ++iter) { std::string strPrefix; std::string path; //如果不是绝对路径,android的则加上"assets/"前缀,表明需要去安装包里找 if (!isAbsolutePath(*iter)) { // Not an absolute path strPrefix = m_strDefaultResRootPath; } //如果路径不是以'/'结尾,则在结尾加上'/' path = strPrefix+(*iter); if (path.length() > 0 && path[path.length()-1] != '/') { path += "/"; } if (!bExistDefaultRootPath && path == m_strDefaultResRootPath) { bExistDefaultRootPath = true; } m_searchPathArray.push_back(path); } if (!bExistDefaultRootPath) { //如果m_strDefaultResRootPath默认路径不在m_searchPathArray,则加入进来 //CCLOG("Default root path doesn't exist,adding it."); m_searchPathArray.push_back(m_strDefaultResRootPath); } } -->> void CCFileUtils::addSearchPath(const char* path_) { std::string strPrefix; std::string path(path_); if (!isAbsolutePath(path)) { // Not an absolute path strPrefix = m_strDefaultResRootPath; } path = strPrefix + path; if (path.length() > 0 && path[path.length()-1] != '/') { path += "/"; } m_searchPathArray.push_back(path); } //移除一个搜索路径: void CCFileUtils::removeSearchPath(const char *path_) { std::string strPrefix; std::string path(path_); if (!isAbsolutePath(path)) { // Not an absolute path strPrefix = m_strDefaultResRootPath; } path = strPrefix + path; if (path.length() > 0 && path[path.length()-1] != '/') { path += "/"; } std::vector<std::string>::iterator iter = std::find(m_searchPathArray.begin(),m_searchPathArray.end(),path); m_searchPathArray.erase(iter); } //移除全部 void CCFileUtils::removeAllPaths() { m_searchPathArray.clear(); } 2、 m_searchResolutionsOrderArray资源路径和上面的一样处理方式,就不说了。 3、 从pszRelativeFile这个文件的相对路径中,得到pszFilename文件的全路径, 其实就是找到pszRelativeFile文件的最后一个'/',然后去这个'/'前的所有字符 + pszFilename即可。 const char* CCFileUtils::fullPathFromRelativeFile(const char *pszFilename,const char *pszRelativeFile) { std::string relativeFile = pszRelativeFile; CCString *pRet = CCString::create(""); pRet->m_sString = relativeFile.substr(0,relativeFile.rfind('/')+1); pRet->m_sString += getNewFilename(pszFilename); return pRet->getCString(); } 4、 //android下的可读写路径 string CCFileUtilsAndroid::getWritablePath() { // Fix for Nexus 10 (Android 4.2 multi-user environment) // the path is retrieved through Java Context.getCacheDir() method string dir(""); //pContext.getFilesDir().getAbsolutePath() java端 string tmp = getFileDirectoryJNI(); //pContext.getFilesDir().getAbsolutePath() if (tmp.length() > 0) { dir.append(tmp).append("/"); return dir; } else { return ""; } }
在3中,我们又分析了几个函数,在这一篇中我们继续分析其他一些函数。 1、android平台 unsigned char* CCFileUtilsAndroid::getFileData(const char* pszFileName,const char* pszMode,unsigned long * pSize) { return doGetFileData(pszFileName,pszMode,pSize,false); } -->> //pszFileName 文件名 //pszMode 读取模式,只有对绝对路径有用,参数就是C API的类型 //pSize 读出的字节大小 //forAsync 同步还是异步 unsigned char* CCFileUtilsAndroid::doGetFileData(const char* pszFileName,unsigned long * pSize,bool forAsync) { unsigned char * pData = 0; if ((! pszFileName) || (! pszMode) || 0 == strlen(pszFileName)) { return 0; } //先获取文件的全路径 string fullPath = fullPathForFilename(pszFileName); if (fullPath[0] != '/') { //如果以"assets/"开头,即文件在安装包里,那么通过压缩文件类从压缩包中读取, //其实android的安装包就是压缩包。 if (forAsync) { pData = s_pZipFile->getFileData(fullPath.c_str(),s_pZipFile->_dataThread); } else { pData = s_pZipFile->getFileData(fullPath.c_str(),pSize); } } else { do { //如果是绝对路径,则通过C API读取。 // 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); unsigned long size; fseek(fp,SEEK_END); size = ftell(fp); fseek(fp,SEEK_SET); pData = new unsigned char[size]; size = fread(pData,sizeof(unsigned char),size,fp); fclose(fp); if (pSize) { *pSize = size; } } while (0); } if (! pData) { std::string msg = "Get data from file("; msg.append(pszFileName).append(") Failed!"); CCLOG("%s",msg.c_str()); } return pData; } 总结:到此为止,cocos2dx-2.X android平台的文件读出我已经通过源码的形式分析完了,做个记录, 以防忘记。