如何在BREW中使用Gzip压缩减少应用程序包的大小?
如何在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图片的压缩效果,对数据文件的压缩效果也比较明显,对声音文件的压缩效果可能要差一些。总体来说,这种方法可以大幅度减少应用程序的大小,而不影响质量,是为存储空间苦恼的开发者的一个不错的选择。