FileUtils如何跨平台查找文件
来源:互联网 发布:上交所网络投票流程 编辑:程序博客网 时间:2024/06/01 08:44
感谢原作者的辛勤劳作:http://blog.csdn.net/Xiejingfa/article/details/50424730
在Cocos2d-x中,如果我们需要创建一个资源,只要调用静态函数Sprite::create(string filename)函数,引擎就会到Resources目录下找到相应的图片并为我们创建一个精灵实例。大家有没有想过,这个过程是怎么实现的呢?我们只是提供了图片的名称,Cocos2d-x怎么就知道我们所需要的图片在Resources目录下呢?为了解开这个疑惑,我们今天就来研究一下Cocos2d-x是怎么实现跨平台查找资源的。
使用的引擎版本是3.8,开发工具是VS2013,我们仍然通过追踪代码的形式学习。好,马上开始我们的揭秘之旅…
我们先从Sprite::create函数入手,首先找到该函数的定义:
代码1:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
在代码1中,我们看到,Cocos2d-x使用了二段构造的方法:先new了一个Sprite对象,然后调用iniWithFile方法进行初始化,最后将创建的Sprite对象放入自动释放池中。
关于自动释放池的知识,涉及到Cocos2d-x的内存管理机制,如果有兴趣,可以看一下我们另一篇博客Cocos2d-x内存管理解析
这段代码中并没有说明Cocos2d-x是如何查找文件路径的,但显然,只有initWithFile函数用到了filename参数,所以,我们继续跳转到该函数的定义中:
代码2:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
在代码2中,依旧没有看到,这里还没有出现文件路径相关的信息。但如同前面分析一样,只有下面这个Director::getInstance()->getTextureCache()->addImage(filename)函数调用中使用到了前面传进来的filename调用,所以我们继续追踪查看addImage的定义
代码3:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
在代码3中,addImage的代码太多,我把后面无关的代码略去。我们看到,这里终于出现了fullpath全路径的字眼,这很可能就是Cocos2d-x实现文件查找的关键所在。
别急着往下看,到这里,我们先暂停一下,来测试一下FileUtils::getInstance()->fullPathForFilename(path)返回的是什么信息。
具体测试方法:我们到HellowWorldScene.cpp的init函数中添加下面代码:
- 1
- 2
- 3
- 1
- 2
- 3
然后运行项目,可以看到输出一下信息:
我们可以看到,上面输出中D:/cocos2d-x-3.8/projects/CocosTest/proj.win32/Debug.win32/部分是Visual Studio项目的调试Debug目录,而HelloWorld.png则是我们指定的文件名称。
到这里,我们是不是几乎可以肯定FileUtils类中的fullPathForFilename方法通过文件名称来查找文件的全路径,来实现文件的跨平台查找。
为了进一步证实我们的猜想,我们继续来查看fullPathForFilename的实现。
为了帮助我们理解fullPathForFilename的工作流程,Cocos2d-x对该函数进行了详细的注释,如下:
代码4:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
在代码4中,我们看到fullPathForFilename函数的作用就是根据给定文件名返回其全路径fullpath。下面,我们就结合注释和具体实现来讲解一下Cocos2d-x是如何查找文件路径的。
1、首先尝试在缓存_fullPathCache中是否能找到该文件的路径
在游戏开发过程中,经常会遇到用同一张图片创建精灵实例的需求。我们想一下,如果每次创建一个精灵我们都要查找一次图片的全路径,这样是不是做了很多无用功?所以Cocos2d-x早就为我们考虑到这一点。引擎利用一个名叫_fullPathCache的成员变量把之前查找过的图片的全路径缓存起来,以后如果用到了同一张图片,则直接从_fullPathCache返回其路径,从而提高游戏的运行效率。
_fullPathCache的定义如下:
代码6:
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
在代码6中,我们看到_fullPathCache实质就是一个unordered_map,并没有什么神秘的。
当调用fullPathForFilename函数时,Cocos2d-x总是先看看该文件是不是已经在加载到_fullPathCache。你是不是已经想到它是怎么查找的了?没错,就是利用unordered_map的find方法,具体如下:
代码7:
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
如何该文件的路径已经存在在_fullPathCache中,就直接返回。如果没有找到呢?那么Cocos2d-x就尝试下面的操作。
2、结合_searchPathArray和_searchResolutionsOrderArray到相关目录下查找
具体过程是:假设我们通过setSearchPaths函数往_searchPathArray添加了两个元素(“/mnt/sdcard/”, “internal_dir/”),还通过setSearchResolutionsOrder函数往_searchResolutionsOrderArray添加了三个元素(“resources-ipadhd/”, “resources-ipad/”, “resources-iphonehd”)。对于给定一张名叫“sprite.png”的图片,在_filenameLookupDict中存在这样一个键值对:key: sprite.png -> value: sprite.pvr.gz,Cocos2d-x就会按照下面的顺序查找:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
如何找到对应的文件,则直接返回该目录,否则返回一个空字符串。
上面的操作中涉及到了两个新的成员变量:_searchPathArray和_searchResolutionsOrderArray,我们先来看看Cocos2d-x源码对这两个变量的解释。
代码7:
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
代码7显示_searchPathArray代表一个查找路径的列表,而且索引值越小,优先级越高。暂时我们也不知道它是干什么用的。
代码8:
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
代码8显示_searchResolutionsOrderArray是包含“分辨率文件夹”的列表,并且也是索引值越小,优先级越高。
对于_searchResolutionsOrderArray的作用则直观一点,一般游戏都需要视频不同分辨率的手机屏幕,_searchResolutionsOrderArray把不同分辨率的图片资源组织到不同的文件夹下,方便游戏的开发。
那_searchPathArray有事做什么用的呢?从它的注释上也没能得到什么有效的信息。别急,我们先做一个简单的测试,在HelloWorldScene.cpp中加入下面代码,把其内容打印出来。
代码9:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
输出结果是这样的:
在这个测试案例中,_searchPathArray中包含一个元素,指向Win32程序的Debug目录。(值得注意的是:我们虽然把图片放在Resources目录下,但是,当发布到不同平台的时候,Cocos2d-x会把相应的文件拷贝到平台对应的目录下,Class目录也是这样)。由于我们没有适配不同分辨率的设备,_searchResolutionsOrderArray默认只有一个空字符串。
所以到这里我们就清楚了,_searchPathArray表示不同平台下放置图片资源的根查找目录,这里,我们不妨称它为“资源根目录”。而_searchResolutionsOrderArray对应相关分辨率目录,结合图片名称filename,Cocos2d-x构造了这样的文件路径:
- 1
- 1
紧接着,Cocos2d-x就到Resources下判断该文件是否存在。
我们到源码中求证一下,下面是fullPathForFilename函数的部分源码
代码10:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
源码面前,了无秘密,无须再解释!
另外,我们看到,Cocos2d-x查找出一个文件的路径后,会把它添加到_fullPathCache中缓存起来。
解决了这个问题是不是就分析完毕了呢?我们还剩下一个问题没有搞清楚:_searchPathArray为什么初始化时就能找到对应平台“资源根目录”呢?
到哪里去查找答案呢?我们不妨大胆猜测一下,一般都会在构造函数或初始化函数中对相关的成员变量初始化。对于FileUtils,它只有一个实例,我们跳转到getInstance函数。
代码10:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
不看不知道,一看“吓一跳”,这个函数竟然定义再CCFileUtils-win32.cpp文件中,而不是FileUtils.cpp文件中!
在代码10中,getInstance返回的是一个FileUtilsApple实例,说明我们已经来到了平台相关的代码中。
- 1
- 1
从FileUtilsWin32定义中可以看到,它是FileUtils的一个子类,涉及到了具体的某个平台。
我们继续查看init函数:
代码11:
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
在代码11中,FileUtilsWin32实例先执行自身的代码,最后调用父类FileUtils的init函数,该函数定义如下:
代码12:
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
在代码12中,我们终于找到了_searchPathArray和_searchResolutionsOrderArray的初始化!但是我们有遇到了一个陌生的变量_defaultResRootPath,继续转到该变量的定义:
代码13:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
代码12显示,_defaultResRootPath就是之前我们说的默认的“资源根目录”。而且,注释中明确说明,_defaultResRootPath的值跟具体的平台相关,所以我们应该到FileUtils-win32中查找Win32平台默认的_defaultResRootPath值。
继续回到上面FileUtils-win32的init函数中,也就是代码11中:
- 1
- 2
- 1
- 2
这两句代码为我们指明了方向,我们转到_checkPath的定义中:
代码14:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
在代码14中,_checkPath是一个静态函数,我们终于看到了Win32平台获取路径的实现。我们可以看到,_checkPath的结果保存在s_resourcePath变量中,s_resourcePath变量又被用来初始化了defaultResRootPath变量。
除此之外,我们还看到FileUtils-win32类中还根据Win32的API重写了父类getPathForFilename、removeFile等文件操作函数。
到此为止,我们终于找到了设置资源路径的函数,明白了_searchPathArray和_searchResolutionsOrderArray是何时,如何被初始化的。大家有没有猜到,Win32平台有FileUtils-win32类实现,那Android、iOS这些平台有没有对应的FileUtils子类呢?答案是肯定的,这里就不带大家一一分析,感兴趣的童鞋可以自己去看看源码。
到这里,我们是否能理解Cocos2d-x的巧妙设计?我们来总结一下Cocos2d-x是如何实现跨平台查找文件的:
- 为了实现跨平台,引擎先抽象出一个父类FileUtils,并定义了s_sharedFileUtils 单例对象。
- 每一个不同的平台都对应了一个名叫FileUtils-xxx的子类,s_sharedFileUtils指向的是该子类的实例对象。在FileUtils-xxx的init函数中,产生该平台对应的资源路径,并添加到_searchPathArray变量中。
- FileUtils-xxx还根据目标平台的特点重写FileUtils函数中平台相关的文件操作函数。这些函数在FileUtils中被定义为virtual function,根据多台的原理,会调用FileUtils-xxx子类的对应函数。
http://blog.csdn.net/Xiejingfa/article/details/50424730
- FileUtils如何跨平台查找文件
- 【Cocos2d-x源码分析】 FileUtils如何跨平台查找文件
- COCOS2D-3.9 FileUtils 分析(三) 文件查找深入
- FileUtils 基本文件操作
- 使用FILEUTILS读写文件
- FileUtils 对文件操作
- 文件工具类FileUtils
- FileUtils文件操作
- FileUtils文件工具类
- Coco2dx文件操作FileUtils
- java FileUtils快速读写文件
- FILEUTILS 介绍(Strtus 文件操作)
- FileUtils 文件管理公共类
- 文件操作工具类FileUtils
- 文件操作工具类FileUtils
- 文件操作工具类FileUtils
- Cocos2d-x 文件管理FileUtils
- 文件转化时可用FileUtils
- 链家面试 全排列
- expdp导出问题亲身体验!
- 解决Python 2.7不能正常使用pip install的问题
- jQuery事件
- Git小白入门常用命令
- FileUtils如何跨平台查找文件
- String相加,竟然可以用单引号 '',并且单引号内是双引号,不用转义
- spark任务在运行时报错
- 单例模式的7种写法
- 2017上海金马五校程序设计竞赛 C :Count the Number
- mount命令+nfs挂载失败原因
- 二分查找算法的实现
- opencv K临近算法(knearest)
- 解决SqlPlus前台程序出现中文乱码的问题