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

上一节 解决了读文件的问题,游戏也跑起来了,可是音效和背景音乐,死活播放不出来。回想一下,的确没有考虑到游戏的音效问题。让我们再折腾一下,播放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

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值