cocos2dx-2.x CCFileUtils文件管理类分析

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/", "internal_dir/") to search paths vector by setSearchPaths,

	//m_searchResolutionsOrderArray
     	and set three elements("resources-ipadhd/", "resources-ipad/", "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, const char* pszMode, 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(), pSize, 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,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)
            {
                *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平台的文件读出我已经通过源码的形式分析完了,做个记录,
以防忘记。



没有更多推荐了,返回首页