如何在BREW中使用Gzip压缩减少应用程序包的大小?
目前在无线增值业务中,涉及图像、声音和数据处理的应用占很大比例,但由于掌上系统的存储能力和处理能力受到很大限制,因此目前BREW应用的大小一般都要求比较校如果图像在整个应用中占很大比例,那么减小图像的存储空间就成为非常迫切的要求。本文从一个开发者的角度阐述了一种解决存储空间限制的方法,使用Gzip压缩工具结合BREW IUnzipAStrem接口可以将资源的大小减小到原来的1/3左右。
大多数BREW开发者都回为手机的存储空间问题而苦恼吧,做个简单的小游戏,图片资源也要占个几十上百KB,有人试图用PNG图片来替代BMP图片,但在实际操作的时候会遇到不少的问题,一些底端的设备根本不支持PNG图片,即使支持在使用的时候也经常会遇到一些莫名其妙的问题。大家仔细看看BREW SDK的API文档,不难发现其中有一个IUnzipAStream接口,高通给出的描述如下:IUnzipAStream接口用于扩展IAStream接口,允许将压缩的IAStream解压并以流的方式读龋(此接口只能解压缩使用GZip算法压缩的数据)。
我的解决方案大致如下:
1. 使用Gzip压缩工具(Linux下使用GZip,Windows下使用7-Zip)压缩BMP图片、声音、数据等资源; 2. 把压缩后的文件作为资源加入到.bar文件中; 3. 在应用程序中使用IUnzipAStream接口来解压相应的数据。 注:PNG图片不能用GZip算法压缩,因为PNG图片本身已经进行了压缩处理,再用GZip算法压缩不会减少,在一些情况下甚至会变大。了解了大体步骤之后我们来简单了解一下GZip算法。GZIP最早由Jean-loup Gailly和Mark Adler创建,用于UNIX系统的文件压缩。我们在Linux中经常会用到后缀为.gz的文件,它们就是GZIP格式的。现今已经成为Internet上使用非常普遍的一种数据压缩格式,或者说一种文件格式。HTTP协议上的GZIP编码是一种用来改进WEB应用程序性能的技术。大流量的WEB站点常常使用GZIP压缩技术来让用户感受更快的速度。
GZIP文件由1到多个“块”组成,实际上通常只有1块。每个块包含头、数据和尾三部分。块的概貌如下:| ID1 | ID2 | CM | FLG | MTIME | XFL | OS | 额外的头字段 | 压缩数据 | CRC232 |ISIZE
这里强调一下和我们应用有关的块的最后一个字段ISIZE,共4个字节,存储了未压缩前数据的长度,这在解压程序中很重要。
应用程序中如何解压缩呢?我们看看下面一段解压的代码。
static uint32 UnZip(theApp *pi,int16 picId,byte **ppBuffer) { uint32size,len,ret; void*pSource; //存放压缩图片的源数据 IMemAStream*memStream; IUnzipAStream*unzipStream; byte*pSourceBuff; //存放压缩图片的有效数据 byte*tempBuff; //存放解压图片的数据 AEEImageInfo info;
ISHELL_CreateInstance(pi->m_App.m_pIShell,AEECLSID_MEMASTREAM,(void **)&memStream); ISHELL_CreateInstance(pi->m_App.m_pIShell,AEECLSID_UNZIPSTREAM,(void **)&unzipStream);
//载入压缩图片的源数据 pSource = ISHELL_LoadResDataEx(pi->m_App.m_pIShell,RES_FILE,picId,RESTYPE_IMAGE,NULL,&size); size = size-*((byte*)pSource); //计算压缩图片有效数据的长度 pSourceBuff = (byte*)MALLOC(size); MEMCPY(pSourceBuff,(byte*)pSource+*((byte *)pSource),size); //从源数据中拷贝出有效数据 ISHELL_FreeResData(pi->m_App.m_pIShell,pSource); //释放载入的源数据 //把内存流和压缩图片相关联 IMEMASTREAM_Set(memStream,pSourceBuff,size,0,FALSE); //把IUNZIPASTREAM对象设置为从内存流中读取数据 IUNZIPASTREAM_SetStream(unzipStream,(IAStream *)memStream); //计算解压后图片的大小 len = (*(pSourceBuff+size-4)) +(*(pSourceBuff+size-3)<<8) +(*(pSourceBuff+size-2)<<16) +(*(pSourceBuff+size-1)<<24); tempBuff = (byte *)MALLOC(len); //读取解压图片的数据流到tempBuff中 ret = IUNZIPASTREAM_Read(unzipStream,tempBuff,len); if (ret>0) { //如果没读完就继续读 while (ret<len) { ret += IUNZIPASTREAM_Read(unzipStream,(byte*)tempBuff+ret,len-ret); } IMEMASTREAM_Release(memStream); //释放内存流 memStream = NULL; IUNZIPASTREAM_Release(unzipStream); //释放解压流 unzipStream = NULL; //图片转换为位图放在内存中 *ppBuffer = CONVERTBMP(tempBuff,&info,FALSE); FREE(tempBuff);
return len; } else if(ret == AEE_STREAM_WOULDBLOCK||ret == 0) { IMEMASTREAM_Release(memStream); memStream = NULL; IUNZIPASTREAM_Release(unzipStream); unzipStream = NULL;
if (tempBuff) { FREE(tempBuff); } return 0; } IMEMASTREAM_Release(memStream); memStream = NULL; IUNZIPASTREAM_Release(unzipStream); unzipStream = NULL;
return 0; }
仔细分析这段代码,大概分为如下几步:
1. 用工厂方法产生内存流的指针memStream 和解压流的指针unzipStream; 2. 从资源文件中读取需要解压图片的数据,存放在pSource中; 3. 将有效的压缩图片数据拷贝到pSourceBuff中,并释放pSource; 4. 把内存流memStream和存放压缩图片数据的缓存相关联; 5. 把解压流和内存流相关联,从内存流memStream中读取压缩图片数据; 6. 根据压缩图片数据的最后4个字节计算出解压后图片的大小,并申请内存空间; 7. 从解压流unzipStream中读取解压后的数据存放到tempBuff中; 8. 释放内存流和解压流,用CONVERTBMP宏把解压后的图片数据转换为本地格式存放到ppBuff中; 9. 释放存放解压图片的内存;
细心一点可能会发现两个问题:
1. 为什么存放压缩图片数据的缓存pSourceBuff没有释放? 2. 存放本地格式位图的缓存ppBuffer事先并没有申请内存空间 其实当pSourceBuff和内存流memStream关联之后,当调用IMEMASTREAM_Release释放内存流时就会自动释放和流关联的数据缓冲区,不需要手动释放。而CONVERTBMP宏也会自动申请大小适合的内存空间。同样声音和数据的解压函数和上面的函数大体相同,只是省去用CONVERTBMP宏进行转换的步骤。
这种方法对图片的压缩效果非常明显,可以达到PNG图片的压缩效果,对数据文件的压缩效果也比较明显,对声音文件的压缩效果可能要差一些。总体来说,这种方法可以大幅度减少应用程序的大小,而不影响质量,是为存储空间苦恼的开发者的一个不错的选择。