cocos2d-x android 直接加载下载到sd的zip里的资源文件(二)

原创 2016年05月30日 16:28:30

上一节 解决了读文件的问题,游戏也跑起来了,可是音效和背景音乐,死活播放不出来。回想一下,的确没有考虑到游戏的音效问题。让我们再折腾一下,播放zip里的音效问题。cocos-x 安卓的背景音乐处理交给了Cocos2dxMusic.java,音效处理交给了Cocos2dxSound.java。别的我们不看,就关注声音资源加载的地方。

先分析一下Cocos2dxMusic.java 的声音资源加载方法

private MediaPlayer createMediaplayer(final String pPath) ;

这个方法就是啦。大概就是判断fullpath路径是否 '/'开头,如果是就加载sd卡声音资源,否则加载apk里的资源。


/**
	 * create mediaplayer for music
	 * 
	 * @param pPath
	 *            the pPath relative to assets
	 * @return
	 */
	private MediaPlayer createMediaplayer(final String pPath) {
		MediaPlayer mediaPlayer = new MediaPlayer();

		try {
			//加载sd卡里的声音资源
			if (pPath.startsWith("/")) {
				final FileInputStream fis = new FileInputStream(pPath);
				mediaPlayer.setDataSource(fis.getFD());
				fis.close();
			} else {
				//加载apk里的声音资源
				final AssetFileDescriptor assetFileDescritor = this.mContext.getAssets().openFd(pPath);
				mediaPlayer.setDataSource(assetFileDescritor.getFileDescriptor(), assetFileDescritor.getStartOffset(), assetFileDescritor.getLength());
			}

			mediaPlayer.prepare();

			mediaPlayer.setVolume(this.mLeftVolume, this.mRightVolume);
		} catch (final Exception e) {
			mediaPlayer = null;
			Log.e(Cocos2dxMusic.TAG, "error: " + e.getMessage(), e);
		}

		return mediaPlayer;
	}

在分析一下Cocos2dxSound.java的声音资源加载方法

->public int playEffect(final String pPath, final boolean pLoop)

->public int preloadEffect(final String pPath)

->public int createSoundIDFromAsset(final String pPath)

也是是判断fullpath路径是否 '/'开头,如果是就加载sd卡声音资源,否则加载apk里的资源。


	public int createSoundIDFromAsset(final String pPath) {
		int soundID = Cocos2dxSound.INVALID_SOUND_ID;

		try {
			if (pPath.startsWith("/")) {
				//加载sd卡声音资源
				soundID = this.mSoundPool.load(pPath, 0);
			} else {
				//加载apk里的声音资源
				soundID = this.mSoundPool.load(this.mContext.getAssets().openFd(pPath), 0);
			}
		} catch (final Exception e) {
			soundID = Cocos2dxSound.INVALID_SOUND_ID;
			Log.e(Cocos2dxSound.TAG, "error: " + e.getMessage(), e);
		}

		// mSoundPool.load returns 0 if something goes wrong, for example a file does not exist
		if (soundID == 0) {
			soundID = Cocos2dxSound.INVALID_SOUND_ID;
		}

		return soundID;
	}
	

回想起来,我们已经有fullpath ,我们要像读取向下面的资源

fullpath = /storage/emulated/0/DonutABC/unitRes/game_22.zip#/res/pub_element/L1U1/audio/pub_unit1_blue_audio.wav


有两种方法:

方法1:直接读取zip里的声音流 让播放器们播放

InputStream 这样的东西java 程序猿们太熟悉不过了,何况是ZipInputStream。我们先将对应的文件用ZipInputStream 读出,然后直接让MediaPlayer 或SoundPool 加载播放流不就可以了吗?可是童话都是骗人的。突然想起邓超演的《美人鱼》,我实在是想不通。好好的白富美在身边不要,偏偏去要一条上半身是人,下半身是鱼的人鱼。没有接口下半辈子能幸福吗!!!实在是想不通。在这个问题上,也是遇到了这样的人鱼。MediaPlayer 和 SoundPool 都没有直接播放流的方法 ,MediaPlayer的setDataSource方法。SoundPool的load方法 都没有播放流的接口。下半辈子不幸福,感觉不会再爱了。好吧换第二个方法

方法2:将zip对应的声音文件临时解压到sd卡里, 然后返回fullpath给 Cocos2dxMusic.java或Cocos2dxSound.java 的加载方法。

新欢没有接口,我们找旧爱。旧爱的接口就是要个声音文件的fullpath吗,太容易满足了。让我们动手改改 

Cocos2dxMusic.java 的 createMediaplayer

	/**
	 * create mediaplayer for music
	 *
	 * @param pPath
	 *            the pPath relative to assets
	 * @return
	 */
	private MediaPlayer createMediaplayer(final String pPath) {
		MediaPlayer mediaPlayer = new MediaPlayer();

		try {
			if (pPath.startsWith("/")) {//改了这里
				String ppPath = PathUtils.getZipFilePath(pPath);
				final FileInputStream fis = new FileInputStream(ppPath);
				mediaPlayer.setDataSource(fis.getFD());
				fis.close();
			} else {
				final AssetFileDescriptor assetFileDescritor = this.mContext.getAssets().openFd(pPath);
				mediaPlayer.setDataSource(assetFileDescritor.getFileDescriptor(), assetFileDescritor.getStartOffset(), assetFileDescritor.getLength());
			}

			mediaPlayer.prepare();

			mediaPlayer.setVolume(this.mLeftVolume, this.mRightVolume);
		} catch (final Exception e) {
			mediaPlayer = null;
			Log.e(Cocos2dxMusic.TAG, "error: " + e.getMessage(), e);
		}

		return mediaPlayer;
	}

 Cocos2dxSound.java 的createSoundIDFromAsset

public int createSoundIDFromAsset(final String pPath) {
		int soundID = Cocos2dxSound.INVALID_SOUND_ID;

		try {
			if (pPath.startsWith("/")) {//改了下面的
				String ppPath = PathUtils.getZipFilePath(pPath);
				soundID = this.mSoundPool.load(ppPath, 0);
			} else {
				soundID = this.mSoundPool.load(this.mContext.getAssets().openFd(pPath), 0);
			}
		} catch (final Exception e) {
			soundID = Cocos2dxSound.INVALID_SOUND_ID;
			Log.e(Cocos2dxSound.TAG, "error: " + e.getMessage(), e);
		}

		// mSoundPool.load returns 0 if something goes wrong, for example a file does not exist
		if (soundID == 0) {
			soundID = Cocos2dxSound.INVALID_SOUND_ID;
		}

		return soundID;
	}


解压的方法 getZipFilePath

	
/***
	 * 获得路径 也许是zip里面的的路径
	 * @param pPath
	 * @return
	 * @throws ZipException
	 * @throws IOException
     */
	public static String getZipFilePath(String pPath) throws ZipException, IOException {
		String zipfilepath = "";
		String filename = "";
		int index = pPath.indexOf("#");
		String ppPath = pPath;
		//是否在zip里
		if(index != -1){
			zipfilepath = pPath.substring(0,index);
			filename = pPath.substring(index+2);
			Log.d("RecordManager","zipfilepath:"+ zipfilepath + "--filename:" + filename);
			String filesavename = filename.replaceAll("/","_");
			ppPath = PathUtils.getTempPath() +filesavename+".temp";
			File filetemp = new File(ppPath);
			//是否有临时解压文件 避免重复解压
			if(!filetemp.exists()){
				filetemp.createNewFile();
				ZipFile file = new ZipFile(zipfilepath);
				FileHeader fileHeader = file.getFileHeader(filename);
				net.lingala.zip4j.io.ZipInputStream zipInputStream =  file.getInputStream(fileHeader);
				FileOutputStream fo =  new FileOutputStream(ppPath);
				byte[] b = new byte[4096];
				int readLine = -1;
				while ((readLine = zipInputStream.read(b)) != -1) {
					fo.write(b,0,readLine);
				}
				fo.close();
				zipInputStream.close();
			}
		}
		return ppPath;
	}

注:我用了开源的zip操作库 zip4j ,我git上有提交 。

好啦。我们这就跑起来。欧啦!声音出来了。这个方案凑合着用,如有更高明的方法,请回复我。


解压方法 c++版


static std::string getZipFilePath(const std::string& fullPath,const std::string& saveDir);
static unsigned char* getFileDataFromZip(const char* pszZipFilePath, const char* pszFileName, unsigned long * pSize);

#include "support/zip_support/ZipUtils.h"
#include "platform/CCCommon.h"
#include "support/zip_support/unzip.h"
#include "platform/CCFileUtils.h"


/**
 * fullpath 音效的绝对路径
 * saveDir  解压到的地方
 * return  解压后的资源返回的路径
 **/
std::string JNItools::getZipFilePath(const std::string& fullPath,const std::string& saveDir){
    unsigned char * pBuffer = NULL;
    unsigned long pSize = 0;
    std::string savepath = fullPath;
    std::string pszFileNameTemp = "";
    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("pszZipFilePath:%s,pszFileNameTemp:%s",pszZipFilePath.c_str(),pszFileNameTemp.c_str());
        

        //替换‘/'
        std::string filename = pszFileNameTemp;
        std::string old_value = "/";
        std::string new_value = "_";
        
        for(string::size_type   pos(0);   pos!=string::npos;   pos+=new_value.length()){
            if((pos=filename.find(old_value,pos))!=string::npos)
                filename.replace(pos,old_value.length(),new_value);
            else
                break;
        }
//        CCLOG("filename:%s",filename.c_str());
        
        savepath =  saveDir + filename + ".temp";
       
        const char* output = savepath.c_str();
        CCLOG("filename:%s",output);
       
            //文件如果存在就不解压了
        int i = access(savepath.c_str(), 0);
        CCLOG("filename:%d",i);
        
        if( i == -1){
            pBuffer = JNItools::getFileDataFromZip(pszZipFilePath.c_str(),pszFileNameTemp.c_str(),&pSize);
                         
            FILE *savefile = fopen(output, "wb");
            fwrite(pBuffer, 1, (size_t)pSize, savefile);
            fflush(savefile);
            fclose(savefile);
            
            delete pBuffer;
        }
        
        
        
        
        
    }
    
    return savepath;

    
}

/**
 * 解压zip 获得资源数据
 **/
unsigned char* JNItools::getFileDataFromZip(const char* pszZipFilePath, const char* pszFileName, unsigned long * pSize)
{
    unsigned char * pBuffer = NULL;
    unzFile pFile = NULL;
    *pSize = 0;
    
    do
    {
//        CCLOG("1");
        CC_BREAK_IF(!pszZipFilePath || !pszFileName);
//         CCLOG("11");
        CC_BREAK_IF(strlen(pszZipFilePath) == 0);
//         CCLOG("1111");
        pFile = unzOpen(pszZipFilePath);
        CC_BREAK_IF(!pFile);
//         CCLOG("2");
        int nRet = unzLocateFile(pFile, pszFileName, 1);
        CC_BREAK_IF(UNZ_OK != nRet);
//         CCLOG("3");
        char szFilePathA[260];
        unz_file_info FileInfo;
        nRet = unzGetCurrentFileInfo(pFile, &FileInfo, szFilePathA, sizeof(szFilePathA), NULL, 0, NULL, 0);
        CC_BREAK_IF(UNZ_OK != nRet);
//         CCLOG("4");
        nRet = unzOpenCurrentFile(pFile);
        CC_BREAK_IF(UNZ_OK != nRet);
//         CCLOG("5");
        pBuffer = new unsigned char[FileInfo.uncompressed_size];
        int CC_UNUSED nSize = unzReadCurrentFile(pFile, pBuffer, FileInfo.uncompressed_size);
        CCAssert(nSize == 0 || nSize == (int)FileInfo.uncompressed_size, "the file size is wrong");
//         CCLOG("6");
        *pSize = FileInfo.uncompressed_size;
        unzCloseCurrentFile(pFile);
    } while (0);
    
    if (pFile)
    {
        unzClose(pFile);
    }
    
    return pBuffer;
}


调方法

public static native  String getZipFilePath(String pPath,String saveDir);

    /**
     * jni 调用
     */
    jstring Java_org_cocos2dx_lib_PathUtils_getZipFilePath(JNIEnv*  env, jobject thiz,jstring path,jstring savedir)
    {
//         CCLOG("filename:%s","Java_org_cocos2dx_lib_PathUtils_getZipFilePath");
        std::string char_path = JniHelper::jstring2string(path);
        std::string char_savedir = JniHelper::jstring2string(savedir);
        std::string str_path = JNItools::getZipFilePath(char_path,char_savedir);
//         CCLOG("----getPath:%s",str_path.c_str());
        env->DeleteLocalRef(path);
        env->DeleteLocalRef(savedir);
        return (env)->NewStringUTF(str_path.c_str());
        
    }


Cocos2dSound.java 修改的地方

public int createSoundIDFromAsset(final String pPath) {
		int soundID = Cocos2dxSound.INVALID_SOUND_ID;

		try {
			if (pPath.startsWith("/")) {
				String ppPath = PathUtils.getZipFilePath(pPath,PathUtils.getTempPath());
				soundID = this.mSoundPool.load(ppPath, 0);
			} else {
				soundID = this.mSoundPool.load(this.mContext.getAssets().openFd(pPath), 0);
			}
		} catch (final Exception e) {
			soundID = Cocos2dxSound.INVALID_SOUND_ID;
			Log.e(Cocos2dxSound.TAG, "error: " + e.getMessage(), e);
		}

		// mSoundPool.load returns 0 if something goes wrong, for example a file does not exist
		if (soundID == 0) {
			soundID = Cocos2dxSound.INVALID_SOUND_ID;
		}

		return soundID;
	}
	

Cocos2dxMusic.java 修改

	/**
	 * create mediaplayer for music
	 *
	 * @param pPath
	 *            the pPath relative to assets
	 * @return
	 */
	private MediaPlayer createMediaplayer(final String pPath) {
		MediaPlayer mediaPlayer = new MediaPlayer();

		try {
			if (pPath.startsWith("/")) {
				String ppPath = PathUtils.getZipFilePath(pPath,PathUtils.getTempPath());
				final FileInputStream fis = new FileInputStream(ppPath);
				mediaPlayer.setDataSource(fis.getFD());
				fis.close();
			} else {
				final AssetFileDescriptor assetFileDescritor = this.mContext.getAssets().openFd(pPath);
				mediaPlayer.setDataSource(assetFileDescritor.getFileDescriptor(), assetFileDescritor.getStartOffset(), assetFileDescritor.getLength());
			}

			mediaPlayer.prepare();

			mediaPlayer.setVolume(this.mLeftVolume, this.mRightVolume);
		} catch (final Exception e) {
			mediaPlayer = null;
			Log.e(Cocos2dxMusic.TAG, "error: " + e.getMessage(), e);
		}

		return mediaPlayer;
	}


整体来看效率没什么提高,就是不用 zip4j 解压了。


还是老样子。下载完整的改动

{{{{{{{{{github传送门}}}}}}}}

本文出处

http://blog.csdn.net/frabbit_on_fire/article/details/51538300

版权声明:本文为博主原创文章,未经博主允许不得转载。

cocos2d-x 横屏、竖屏、读写sd卡

一、横屏、竖屏设置         AndroidManifest.xml文件中,         screenOrientation="landscape" 为横屏, screenO...
  • he_wen_jian
  • he_wen_jian
  • 2013年12月24日 15:59
  • 2180

cocos2dx对于android中sd卡的访问方式

今天在弄项目的时候涉及到cocos2dx引擎需要访问sdcard上的资源,目前发现两种比较简单的方法: 1、直接使用硬编码的方式来进行访问,例如“/storage/sdcard0/NetAnswer...
  • brucebruce112
  • brucebruce112
  • 2015年03月06日 21:54
  • 965

cocos2d-x android 直接加载下载到sd的zip里的资源文件(一)

最近公司要做的一个cocos-x项目,这个项目用的是2.2.6版的cocos-x c++ 版,cocos比较老的版本。由于打包出来的apk超过了300M,而且资源无法热更新。面临这两条问题。我们讨论了...
  • bobai2009
  • bobai2009
  • 2016年05月27日 20:31
  • 4538

cocos2dx 3.10 读取加密压缩ZIP文件

0,废话 最近需要把资源打包到压缩文件并加密,一开始是上网搜索怎么读取加密的ZIP文件的,很遗憾没有搜到读取加密的,只有一些读取没有加密的,于是就想着自己实现,刚开始网上了解了一下,大家都是使用z...
  • u014772578
  • u014772578
  • 2016年05月06日 10:18
  • 3999

assets里的zip压缩文件并解压到指定路径下保存

有时候发布app,在assets中会自带一些压缩包,打开应用解压放在用户手机sd卡中, 解压这一块就是下面的代码,要判断解压出来的是直接的文件还是一个文件夹,并存储到指定路径下面就好 publ...
  • chenaini119
  • chenaini119
  • 2015年12月25日 15:39
  • 1155

[zip]android下实现直接读取zip中的文件

不用解压,直接读取zip中指定的文件,例如文件/sdcard/a.zip中有文件z.txt,读取步骤如下: int zfd = zip_open("/sdcard/a.zip/z.txt");...
  • victoryckl
  • victoryckl
  • 2013年02月03日 22:43
  • 3261

duilib加载资源

duilib给我提供了4中加载资源的方式: 1、直接读xml文件 2、读zip资源压缩包 3、读rc资源 4、读dll中的rc资源   前三种方法比较简单,稍微复杂...
  • JUST_DO_1122
  • JUST_DO_1122
  • 2016年10月10日 14:24
  • 1719

Android 不解压直接读取zip包

不解压直接读取的zip里的资源文件
  • mackkill
  • mackkill
  • 2017年04月20日 17:12
  • 1712

【Cocos2d-x】 Cocos2d-x 3.X 异步加载plist图片资源

1、异步加载plist cocos2dx-x本身帮我们封装了一个名为addImageAsync的函数帮我们实现图片的异步加载。 /** Returns a Texture2D object gi...
  • Xiejingfa
  • Xiejingfa
  • 2015年07月17日 20:03
  • 2662

IOS下载资源zip到本地然后读取

欢迎访问我的接口小站:www.chinadev.cn 思路是 1.ios下载服务器上的zip资源包(图片,声音等经过zip压缩的资源包)到本地 2.解压zip到程序目录 3.从程序目录加载资源...
  • zzzili
  • zzzili
  • 2013年06月05日 11:21
  • 4439
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:cocos2d-x android 直接加载下载到sd的zip里的资源文件(二)
举报原因:
原因补充:

(最多只允许输入30个字)