最近把win32和ios上运行成功的游戏移植到android时,程序直接挂了,查到原因是:使用fopen读取assets里的数据会导致应用crash,因为数据已经被压缩打包进apk文件里了。
解决办法:
1.使用cocos2d-x提供的CCFileUtils工具类
2.把assets中的文件读取出来复制到/data/data/you_app_packagename/或者sd卡目录下,然后再使用fopen函数读取。
下面来看看如何使用CCFileUtils工具类读取assets目录下的文件,代码如下:

//获得文件在系统的绝对路径
const char *filepath = CCFileUtils::fullPathFromRelativePath(filename);
//读取的字节数,读取失败则为0
unsigned long len = 0;
//读取的内容
unsigned char *data = CCFileUtils::getFileData(filepath, "r", &len);;
//记得释放内存
if(len >0 && data) delete[] data;

cocos2d-x会根据具体的平台调用CCFileUtils对应的实现类,win32是CCFileUtils_win32.cpp,android平台是CCFileUtils_andorid.cpp,
下面是CCFileUtils_andorid.cpp文件的部分源码:

unsigned char* CCFileUtils::getFileData(const char* pszFileName, const char* pszMode, unsigned long * pSize)
{	
	unsigned char * pData = 0;
	string fullPath(pszFileName);

	if ((! pszFileName) || (! pszMode))
	{
		return 0;
	}

	if (pszFileName[0] != '/')
	{
		// read from apk
		fullPath.insert(0, "assets/");
		pData =  CCFileUtils::getFileDataFromZip(s_strResourcePath.c_str(), fullPath.c_str(), pSize);
	}
	else
	{
		do 
		{
			// read rrom other path than user set it
			FILE *fp = fopen(pszFileName, 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 && getIsPopupNotify())
    {
        std::string title = "Notification";
        std::string msg = "Get data from file(";
        msg.append(fullPath.c_str()).append(") failed!");
        CCMessageBox(msg.c_str(), title.c_str());
    }

    return pData;
}

当读取的文件名称是类似于”/test.txt”时,是使用fopen()函数的,这不是读取apk文件里的文件,是没有问题的,比如读取/data/data/packagename/xxx.txt。
最近我在android平台使用fopen()函数时,出现了很多问题,如下面代码:

FILE* m_pFile = fopen(filepath,"r");
char ch = getc(m_pFile);
while(ch != EOF)
{
... 
ch=getc(m_pFile);
}

在android平台这个while循环会无限循环,导致模拟器黑屏了。原来读到文件末尾时,ch的值为255,不等于EOF,改成while(ch != EOF && ch!= 255)就行了。
当文件名称是”test.txt”时,此时调用的是getFileDataFromZip()这个函数,读取apk压缩包assets里的文件。

下面附上把apk中assets目录下的文件拷贝到/data/data/${packagename}/下的代码(不过复制之前你应该先判断一下文件是否存在):

bool isFileExist(const char* pFileName)
{
	if( !pFileName ) return false;
	//strFilePathName is :/data/data/ + package name
	std::string filePath = CCFileUtils::getWriteablePath();
	filePathName += pFileName;

	FILE *fp = fopen(filePath.c_str(),"r");
	if(fp)
	{
		fclose(fp);
		return true;
	}
	return false;
}

void copyData(const char* pFileName)
{
	std::string strPath = CCFileUtils::fullPathFromRelativePath(pFileName);
	unsigned long len = 0;
	unsigned char *data = NULL;

	data = CCFileUtils::getFileData(strPath.c_str(),"r",&len);
	std::string destPath = CCFileUtils::getWriteablePath();
	destPath += pFileName;
	FILE *fp = fopen(destPath.c_str(),"w+");
	fwrite(data,sizeof(char),len,fp);
	fclose(fp);
	delete []data;
	data = NULL;
}

cocos2d-x手游性能优化总结

近段时间在使用cocos2d-x开发2D手游,技术方案使用的是cocos2d-x+lua,因为游戏使用的是cocos2d-x 2.1.5版本,有些优化方案在最新版的cocos2d-x版本已经实现...

阅读全文

cocos2dx-html5 实现网页版flappy bird游戏

我也是第一次使用cocos2d_html5,对js和html5也不熟,看引擎自带的例子和引擎源码,边学边做,如果使用过cocos2d-x的话,完成这个游戏还是十分简单的。游戏体...

阅读全文

【cocos2d-x开发实战 特训99-终结篇】移植到android平台和添加admob广告

上一篇已经完成特性99在win32平台下的开发,现在把它移植到android上,首先修改Android.mk文件,内容如下: LOCAL_PATH := $(call my-dir) include $(CLEAR_...

阅读全文

6 条评论

  1. 楼主,你的方法只能拷贝指定文件夹。
    我现在想实现一个解压功能,遍历assets下所有资源,解压到可写路径,
    安卓assetmanager有个list接口,2dx要如何做?

  2. 请问下,我把assets目录下的一个zip文件(scrip。zip)考到/data/data 可写跟目录下成功,但是考到根目录的子文件夹下就失败是怎么回事?读写方式跟楼主一致

    1. très beau rôti qui me donne très envie de le tester, en plus j'adore la sauge, même si je n'en utilise pas trop souvent, c'est une herbe que j'adore…mais que je ne trouve pas forcément facilement fraiche (et oui, j'ai pas de jardin, alors c'est plus compliqué !)

  3. 使用cocos2d-x 开发游戏,安装apk时,如何在SD中创建一个文件夹,把游戏的资源文件(图片,音乐等)放在这个新的文件夹中?

    1. 进入游戏时,利用sdk创建文件夹比较方便也安全,cocos2d-x通过jni调用java层函数就行,拷贝操作上面博文里已经实现了。

      1. 谢谢了。还有个问题想问一下,使用coco2d-x 如何设置资源路径为SD上的某一个文件夹?如CCFileUtils::sharedFileUtils()->setResourceDirectory(“/sdcard/xxxx/resource”);资源从resource下面读取。

欢迎留言