AndroidBitmap(位图)详解
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
AndroidBitmap(位图)详解
⼀、背景
在Android开发中,任何⼀个APP都离不开图⽚的加载和显⽰问题。
这⾥的图⽚来源分为三种:项⽬图⽚资源⽂件(⼀般为res/drawable⽬录下的图⽚⽂件)、⼿机本地图⽚⽂件、⽹络图⽚资源等。
图⽚的显⽰我们⼀般采⽤ImageView作为载体,通过ImageView的相应API即可设置其显⽰的图⽚内容。
我们知道:如果是需要展⽰项⽬中的图⽚资源⽂件,我们只需要调⽤ImageView的setImageResource(int id)⽅法并传⼊该图⽚资源的id(⼀般为R.drawable.xxx)即可。
但是如果是需要展⽰⼿机本地的某张图⽚或者⽹络上的某个图⽚资源,⼜该怎么办呢?——问题A
为了回答问题A,我们先思考⼀个更深的问题B:Android中是如何将某⼀张图⽚的内容加载到内存中继⽽由ImageView显⽰的呢?
我们知道:如果我们想通过TextView展⽰⼀个本地txt⽂件的内容,我们只需要由该⽂件创建并包装⼀个输⼊流对象。
通过该输⼊流对象即可得到⼀个代表该⽂件内容的字符串对象,再将该字符串对象交由TextView展⽰即可。
换句话说,这个txt⽂件的内容在内存中的表达形式就是这个字符串对象。
类推⼀下,虽然图⽚⽂件也是⽂件,但是我们显然不可能对图⽚⽂件也采⽤这种⽅式:即通过该图⽚建⽴并包装⼀个输⼊流对象再获取⼀个字符串对象。
毕竟⽆论如何我们都⽆法将某个图⽚的内容表⽰为⼀个字符串对象(细想⼀下就知道了,你能通过⼀段话100%准确地描述⼀张图⽚吗?显然不现实)。
那么,这就引⼊了问题C:既然字符串对象不⾏,那么我们该以哪种对象来在内存中表⽰某个图⽚的内容呢?答案就是:Bitmap对象!
⼆、基本概述
Bitmap,即位图。
它本质上就是⼀张图⽚的内容在内存中的表达形式。
那么,Bitmap是通过什么⽅式表⽰⼀张图⽚的内容呢?
Bitmap原理:从纯数学的⾓度,任何⼀个⾯都由⽆数个点组成。
但是对于图⽚⽽⾔,我们没必要⽤⽆数个点来表⽰这个图⽚,毕竟单独⼀个微⼩的点⼈类⾁眼是看不清的。
换句话说,由于⼈类⾁眼的能⼒有限,我们只需要将⼀张图⽚表⽰为有限但⾜够多的点即可。
点的数量不能⽆限,因为⽆限的点信息量太⼤⽆法存储;但是点的数量也必须⾜够多,否则视觉上⽆法形成连贯性。
这⾥的点就是像素。
⽐如说,某个1080*640的图⽚,这⾥的像素总数即为1080X640个。
将图⽚内容表⽰为有限但⾜够多的像素的集合,这个“⽆限→有限”的思想极其迷⼈。
所以,我们只需要将每个像素的信息存储起来,就意味着将整个图⽚的内容进⾏了表达。
像素信息:每个像素的信息,⽆⾮就是ARGB四个通道的值。
其中,A代表透明度,RGB代表红绿蓝三种颜⾊通道值。
每个通道的值范围在0~255之间,即有256个值,刚好可以通过⼀个字节(8bit)进⾏表⽰。
所以,每个通道值由⼀个字节表⽰,四个字节表⽰⼀个像素信息,这似乎是最好的像素信息表⽰⽅案。
但是这⾥忽略了两个现实的需求问题:
①在实际需求中,我们真的需要这么多数量的颜⾊吗?上述⽅案是256X256X256种。
有的时候,我们并不需要这么丰富的颜⾊数量,所以可以适当减少表⽰每个颜⾊通道的bit位数。
这么做的好处是节省空间。
也就是说,每个颜⾊通道都采⽤8bit来表⽰是代表全部颜⾊值的集合;⽽我们可以采⽤少于8bit的表⽰⽅式,尽管这会缺失⼀部分颜⾊值,但是只要颜⾊够⽤即可,并且这还可以节省内存空间。
②我们真的需要透明度值吗?如果我们需要某个图⽚作为背景或者图标,这个图⽚透明度A通道值是必要的。
但是如果我们只是普通的图⽚展⽰,⽐如拍摄的照⽚,透明度值毫⽆意义。
细想⼀下,你希望你⼿机⾃拍的照⽚透明或者半透明吗?hell no! 因此,透明度这个通道值是否有必要表⽰也是根据需求⾃由变化的。
具体每个像素点存储ARGB值的⽅案介绍,后⾯会详细介绍。
总结:Bitmap对象本质是⼀张图⽚的内容在内存中的表达形式。
它将图⽚的内容看做是由存储数据的有限个像素点组成;每个像素点存储该像素点位置的ARGB值。
每个像素点的ARGB值确定下来,这张图⽚的内容就相应地确定下来了。
现在回答⼀下问题A和问题B:Android就是将所有的图⽚资源(⽆论是何种来源)的内容以Bitmap对象的形式加载到内存中,再通过ImageView的setImageBitmap(Bitmap b)⽅法即可展⽰该Bitmap对象所表⽰的图⽚内容。
三、详细介绍
1、Bitmap.Config
Config是Bitmap的⼀个枚举内部类,它表⽰的就是每个像素点对ARGB通道值的存储⽅案。
取值有以下四种:
ARGB_8888:这种⽅案就是上⾯所说的每个通道值采8bit来表⽰,每个像素点需要4字节的内存空间来存储数据。
该⽅案图⽚质量是最⾼的,但是占⽤的内存也是最⼤的
ARGB_4444:这种⽅案每个通道都是4位,每个像素占⽤2个字节,图⽚的失真⽐较严重。
⼀般不⽤这种⽅案。
RGB_565:这种⽅案RGB通道值分别占5、6、5位,但是没有存储A通道值,所以不⽀持透明度。
每个像素点占⽤2字节,是ARGB_8888⽅案的⼀半。
ALPHA_8:这种⽅案不⽀持颜⾊值,只存储透明度A通道值,使⽤场景特殊,⽐如设置遮盖效果等。
⽐较分析:⼀般我们在ARGB_8888⽅式和RGB_565⽅式中进⾏选取:不需要设置透明度时,⽐如拍摄的照⽚等,RGB_565是个节省内存空间的不错的选择;既要设置透明度,对图⽚质量要求⼜⾼,就⽤ARGB_8888。
2、Bitmap的压缩存储
Bitmap是图⽚内容在内存中的表⽰形式,那么如果想要将Bitmap对象进⾏持久化存储为⼀张本地图⽚,需要对Bitmap对象表⽰的内容进⾏压缩存储。
根据不同的压缩算法可以得到不同的图⽚压缩格式(简称为图⽚格式),⽐如GIF、JPEG、BMP、PNG和WebP等。
这些图⽚的(压缩)格式可以通过图⽚⽂件的后缀名看出。
换句话说:Bitmap是图⽚在内存中的表⽰,GIF、JPEG、BMP、PNG和WebP等格式图⽚是持久化存储后的图⽚。
内存中的Bitmap到磁盘上的GIF、JPEG、BMP、PNG和WebP等格式图⽚经过了”压缩”过程,磁盘上的GIF、JPEG、BMP、PNG和WebP等格式图⽚到内存中的Bitmap经过了“解压缩”的过程。
那么,为什么不直接将Bitmap对象进⾏持久化存储⽽是要对Bitmap对象进⾏压缩存储呢?这么做依据的思想是:当图⽚持久化保存在磁盘上时,我们应该尽可能以最⼩的体积来保存同⼀张图⽚的内容,这样有利于节省磁盘空间;⽽当图⽚加载到内存中以显⽰的时候,应该将磁盘上压缩存储的图⽚内容完整地展开。
前者即为压缩过程,⽬的是节省磁盘空间;后者即为解压缩过程,⽬的是在内存中展⽰图⽚的完整内容。
3、有损压缩和⽆损压缩
Bitmap压缩存储时的算法有很多种,但是整体可分为两类:有损压缩和⽆损压缩。
①有损压缩
有损压缩的基本依据是:⼈的眼睛对光线的敏感度远⾼于对颜⾊的敏感度,光线对景物的作⽤⽐颜⾊的作⽤更为重要。
有损压缩的原理是:保持颜⾊的逐渐变化,删除图像中颜⾊的突然变化。
⽣物学中的⼤量实验证明,⼈类⼤脑会⾃发地利⽤与附近最接近的颜⾊来填补所丢失的颜⾊。
有损压缩的具体实现⽅法就是删除图像中景物边缘的某些颜⾊部分。
当在屏幕上看这幅图时,⼤脑会利⽤在景物上看到的颜⾊填补所丢失的颜⾊部分。
利⽤有损压缩技术,某些数据被有意地删除了,并且在图⽚重新加载⾄内存中时这些数据也不会还原,因此被称为是“有损”的。
有损压缩技术可以灵活地设置压缩率。
⽆可否认,利⽤有损压缩技术可以在位图持久化存储的过程中⼤⼤地压缩图⽚的存储⼤⼩,但是会影响图像质量,这⼀点在压缩率很⾼时尤其明显。
所以需要选择恰当的压缩率。
②⽆损压缩
⽆损压缩的基本原理是:相同的颜⾊信息只需保存⼀次。
具体过程是:⾸先会确定图像中哪些区域是相同的,哪些是不同的。
包括了重复数据的区域就可以被压缩,只需要记录该区域的起始点即可。
从本质上看,⽆损压缩的⽅法通过删除⼀些重复数据,也能在位图持久化存储的过程中减少要在磁盘上保存的图⽚⼤⼩。
但是,如果将该图⽚重新读取到内存中,重复数据会被还原。
因此,⽆损压缩的⽅法并不能减少图⽚的内存占⽤量,如果要减少图⽚占⽤内存的容量,就必须使⽤有损压缩⽅法。
⽆损压缩⽅法的优点是能够⽐较好地保存图像的质量,但是相对来说这种⽅法的压缩率⽐较低。
对⽐分析:有损压缩压缩率⾼⽽且可以灵活设置压缩率,并且删除的数据不可还原,因此可以减少图⽚的内存占⽤,但是对图⽚质量会有⼀定程度的影响;⽆损压缩可以很好地保存图⽚质量,也能保证⼀定的压缩率虽然没有有损压缩那么⾼,并且⽆损压缩删除的数据在重新加载⾄内存时会被还原,因此不可以减少图⽚的内存占⽤。
4、位深与⾊深
我们知道了图⽚在内存中和在磁盘上的两种不同的表⽰形式:前者为Bitmap,后者为各种压缩格式。
这⾥介绍⼀下位深与⾊深的概念:
①⾊深
⾊深指的是每⼀个像素点⽤多少bit来存储ARGB值,属于图⽚⾃⾝的⼀种属性。
⾊深可以⽤来衡量⼀张图⽚的⾊彩处理能⼒(即⾊彩丰富程度)。
典型的⾊深是8-bit、16-bit、24-bit和32-bit等。
上述的Bitmap.Config参数的值指的就是⾊深。
⽐如ARGB_8888⽅式的⾊深为32位,RGB_565⽅式的⾊深是16位。
②位深
位深指的是在对Bitmap进⾏压缩存储时存储每个像素所⽤的bit数,主要⽤于存储。
由于是“压缩”存储,所以位深⼀般⼩于或等于⾊深。
举个例⼦:某张图⽚100像素*100像素⾊深32位(ARGB_8888),保存时位深度为24位,那么:
该图⽚在内存中所占⼤⼩为:100 * 100 * (32 / 8) Byte
在⽂件中所占⼤⼩为 100 * 100 * ( 24/ 8 ) * 压缩率 Byte
5、常见的压缩格式
Bitmap的压缩格式就是最终持久化存储得到的图⽚格式,⼀般由后缀名即可看出该图⽚采⽤了何种压缩⽅式。
不同的压缩⽅式的压缩算法不⼀样。
常见的主要有:
①Gif
Gif是⼀种基于LZW算法的⽆损压缩格式,其压缩率⼀般在50%左右。
Gif可插⼊多帧,从⽽实现动画效果。
因此Gif图⽚分为静态GIF和动画GIF两种GIF格式。
由于Gif以8位颜⾊压缩存储单个位图,所以它最多只能⽤256种颜⾊来表现物体,对于⾊彩复杂的物体它就⼒不从⼼了。
因此Gif不适合⽤于⾊彩⾮常丰富的图⽚的压缩存储,⽐如拍摄的真彩图⽚等。
②BMP
BMP是标准图形格式,它是包括Windows在内多种操作系统图像展现的终极形式。
其本质就是Bitmap对象直接持久化保存的位图⽂件格式,由于没有进⾏压缩存储,因此体积⾮常⼤,故⽽不适合在⽹络上传输。
同时也是因为这种格式是对Bitmap对象的直接存储⽽没有进⾏压缩,因此我们在讨论压缩格式时往往忽略这⼀种。
③PNG
PNG格式本⾝的设计⽬的是替代GIF格式,所以它与GIF 有更多相似的地⽅。
PNG格式也属于⽆损压缩,其位深为32位,也就是说它⽀持所有的颜⾊类型。
同样是⽆损压缩,PNG的压缩率⾼于Gif格式,⽽且PNG⽀持的颜⾊数量也远⾼于Gif,因此:如果是对静态图⽚进⾏⽆损压缩,优先使⽤PNG取代Gif,因为PNG压缩率⾼、⾊彩好;但是PNG不⽀持动画效果。
所以Gif仍然有⽤武之地。
PNG缺点是:由于是⽆损压缩,因此PNG⽂件的体积往往⽐较⼤。
如果在项⽬中多处使⽤PNG图⽚⽂件,那么在APP瘦⾝时需要对PNG⽂件进⾏优化以减少APP体积⼤⼩。
具体做法后⾯会详细介绍。
④JPEG
JPEG是⼀种有损压缩格式,JPEG图⽚以24位颜⾊压缩存储单个位图。
也就是说,JPEG不⽀持透明通道。
JPEG也不⽀持多帧动画。
因为是有损压缩,所以需要注意控制压缩率以免图⽚质量太差。
JPG和JPEG没有区别,全名、正式扩展名是JPEG。
但因DOS、Windows95等早期系统采⽤的8.3命名规则只⽀持最长3字符的扩展名,为了兼容采⽤了.jpg。
也因历史习惯和兼容性的考虑,.jpg⽬前更流⾏。
JPEG2000作为JPEG的升级版,其压缩率⽐JPEG⾼约30%左右,同时⽀持有损和⽆损压缩。
JPEG2000格式有⼀个极其重要的特征在于它能实现渐进传输,即先传输图像的轮廓,然后逐步传输数据,不断提⾼图像质量,让图像由朦胧到清晰显⽰。
此外,JPEG2000还⽀持所谓的“感兴趣区域”特性,也就是可以任意指定影像上感兴趣区域的压缩质量;另外,JPEG2000还可以选择指定的部分先解压缩来加载到内存中。
JPEG2000和JPEG相⽐优势明显,且向下兼容,因此可取代传统的JPEG格式。
⑤WebP
WebP 是 Google 在 2010 年发布的图⽚格式,希望以更⾼的压缩率替代 JPEG。
它⽤ VP8 视频帧内编码作为其算法基础,取得了不错的压缩效果。
WebP⽀持有损和⽆损压缩、⽀持完整的透明通道、也⽀持多帧动画,并且没有版权问题,是⼀种⾮常理想的图⽚格式。
WebP⽀持动图,基本取代gif。
WebP不仅集成了PNG、JPEG和Gif的所有功能,⽽且相同质量的⽆损压缩WebP图⽚体积⽐PNG⼩⼤约26%;如果是有损压缩,相同质量的WebP图⽚体积⽐JPEG⼩25%-34%。
很多⼈会认为,既然WebP功能完善、压缩率更⾼,那直接⽤WebP取代上述所有的图⽚压缩格式不就⾏了吗?其实不然,WebP也有其缺点:我们知道JPEG是有损压缩⽽PNG是⽆损压缩,所以JPEG的压缩率⾼于PNG;但是有损压缩的算法决定了其压缩时间⼀定是⾼于⽆损压缩的,也就是说JPEG的压缩时间⾼于PNG。
⽽WebP⽆论是⽆损还是有损压缩,压缩率都分别⾼于PNG和JPEG;与其相对应的是其压缩时间也⽐它们长的多。
经测试,WebP图⽚的编码时间⽐JPEG长8倍。
可以看出,时间和空间是⼀对⽭盾;如果想要节省更多的空间,必然要付出额外的时间;如果想要节省时间,那么必然要付出空间的代价。
这取决于我们在实际中对于时空不同的需求程度来做出选择。
不管怎么说,WebP还是⼀种强⼤的、理想的图⽚压缩格式,并且借由 Google 在⽹络世界的影响⼒,WebP 在⼏年的时间内已经得到了⼴泛的应⽤。
看看你⼿机⾥的 App:微博、微信、QQ、淘宝等等,每个 App ⾥都有 WebP 的⾝影。
另外,WebP是Android4.0才引⼊的⼀种图⽚压缩格式,如果想要在Android4.0以前的版本⽀持WebP格式的图⽚,那么需要借助于第三⽅库来⽀持WebP格式图⽚,例如:webp-android-backport函数库,该开源项⽬在GitHub地址为:当然考虑到⼀般的Android开发中只需要向下兼容到Android4.0即可,所以也可以忽略这个问题。
⽬前来说,以上所述的五种压缩格式,Android操作系统都提供了原⽣⽀持;但是在上层能直接调⽤的编码⽅式只有 JPEG、PNG、WebP 这三种。
具体的,可以查看Bitmap类的枚举内部类CompressFormat类的枚举值来获取上层能调⽤的图⽚编码⽅式。
你会发现枚举值也是JPEG、PNG和WEBP三种。
如果我们想要在应⽤层使⽤Gif格式图⽚,需要⾃⾏引⼊第三⽅函数库来提供对Gif格式图⽚的⽀持。
不过⼀般我们⽤WebP取代Gif。
因此,我们只需要⽐较分析PNG、JPEG、WebP这三种压缩格式即可。
⽐较分析:
①对于摄影类等真彩图⽚:因为我们对这类⾊彩丰富的图⽚的透明度没有要求(⼀般默认为不透明),可以采⽤JPEG有损压缩格式,因为JPEG本⾝就不⽀持透明度,⽽且因为是有损压缩,所以尽管会牺牲⼀丢丢照⽚的质量但是可以⼤⼤减少体积。
如果⾮要采⽤PNG格式,那么⾸先因为PNG⽀持透明度通道,所以明明不必要的透明度值却会被存储;其次因为是⽆损压缩,所以压缩率不会很⾼从⽽导致保存的图⽚⾮常⼤!综上⽐较,建议采⽤JPEG格式,不要⽤PNG格式。
JPEG格式可以与Bitmap.Config参数值为RGB_565搭配使⽤,这是⼀个理想的设置。
②对于logo图标、背景图等图⽚:这类图⽚的特点是往往是有⼤块的颜⾊相同的区域,这与⽆损压缩的思路不谋⽽合(即删除重复数据)。
⽽且这类图⽚对透明度是有要求的,因此可以采⽤PNG⽆损压缩格式;尽管使⽤PNG格式会让图⽚有点⼤,但是可以在后续进⾏PNG图⽚优化以对APP体积进⾏瘦⾝。
如果⾮要采⽤JPEG格式,那么由于有损压缩的原理(利⽤⼈脑的⾃动补全机制),可能会随机地丢失⼀些线条导致最终的图⽚完全不是想要的效果。
综上⽐较,建议使⽤PNG格式,不要⽤JPEG格式。
PNG格式可以与Bitmap.Config参数值为ARGB_8888搭配使⽤,这是⼀个理想的设置。
当然,以上两种情况,我们都可以使⽤WebP取代PNG或JPEG,如果我们想要这么做的话。
如果你的项⽬中对空间的需求程度更⾼,你完全有理由这么做。
但是如果你对空间需求程度还OK,你也可以选择分情况使⽤PNG或JPEG格式。
6、图⽚优化
图⽚优化属于Android性能优化的⼀种,这⾥主要是针对PNG图⽚的⼤⼩进⾏优化,毕竟PNG这种⽆损压缩格式往往会导致图⽚都⽐较⼤。
除⾮你的项⽬已经全⾯⽀持了WebP格式,否则对PNG格式图⽚的优化都会是你必须考虑的⼀点,这有利于减少APP的体积⼤⼩。
对PNG图⽚进⾏优化的思想是:减少PNG图⽚的体积,常⽤⽅式有:
①⽆损压缩⼯具ImageOptim
ImageOptim是⼀种⽆损压缩⼯具,所以你不⽤担⼼利⽤该⼯具对PNG图⽚进⾏压缩后图⽚质量会受影响。
它的压缩原理是:优化PNG压缩参数,移除冗余元数据以及⾮必需的颜⾊配置⽂件等,在不牺牲图⽚质量的前提下,既减少了PNG图⽚的⼤⼩,⼜提⾼了其加载的速度。
ImageOptim⼯具的⽹址为:
②有损压缩⼯具ImageAlpha
ImageAlpha与ImageOptim是同⼀个作者,不过ImageAlpha属于有损压缩,因此图⽚质量会受到影响。
所以使⽤ImageAlpha对PNG图⽚进⾏压缩后,必须让设计师检视⼀下优化后的PNG图⽚,以免影响APP的视觉效果。
但是ImageAlpha的优点是可以极⼤减少PNG图⽚的体积⼤⼩。
ImageAlpha⼯具的⽹址为:
③有损压缩TinyPNG
前⾯两个⼯具是应⽤程序,TinyPNG是⼀个Web站点。
你可以上传原PNG图⽚,它对PNG图⽚压缩后你就可以下载优化后的结果了。
因为TinyPNG也是有损压缩,所以优缺点同②
TinyPNG的⽹址为:
以上⽅案都属于对PNG图⽚进⾏⼆次压缩(有的是有损有的是⽆损),我们需要在图⽚质量和图⽚⼤⼩这对⽭盾中根据实际情况进⾏选择。
④PNG/JPEG转换为WebP
如果不想对PNG图⽚进⾏⼆次压缩,可以考虑直接将其替换为WebP格式的图⽚。
另外,我们对JPEG格式的图⽚也可以这么替换。
毕竟WebP⽆论是与PNG还是与JPEG格式想⽐,压缩后体积⼤⼩都⼩很多。
WebP转换⼯具有:
智图,这是⼀个图⽚优化平台,地址为:
iSparta,这是⼀个针对PNG图⽚的⼆次压缩和格式转换⼯具,地址为:
⑤使⽤NinePatch格式的PNG图
.9.png图⽚格式简称为NinaPatch图,本质上仍然是PNG格式图⽚。
不过它的优点是体积⼩、拉伸不变形,能够很好地适配Android各种机型。
我们可以利⽤Android Studio提供的功能,右键⼀张PNG图⽚点击“create 9=Patch File”即可完成转换。
总结:⽆论是⼆次压缩还是格式转换,⽆论是有损⼆次压缩还是⽆损⼆次压缩,我们都需要根据实际需求进⾏⽅案和⼯具的选择。
我们已经知道了Android中图⽚内存中的表⽰形式(Bitmap)和磁盘上的表⽰形式(各种压缩格式),以及⼆者的关系(压缩和解压缩的过程)。
下⾯具体看看Bitmap的使⽤⽅式和注意事项,毕竟磁盘上存储的图⽚终究还是要加载到内存中以Bitmap的形式进⾏展⽰的。
四、Bitmap的简单使⽤
先看看Bitmap的简单使⽤⽅式:
1、Bitmap的加载⽅法
Bitmap的⼯⼚类BitmapFactory提供了四类静态⽅法⽤于加载Bitmap对象:decodeFile、decodeResource、decodeStream、decodeByteArray。
分别代表从本地图⽚⽂件、项⽬资源⽂件、流对象(可以是⽹络输⼊流对象或本地⽂件输⼊流对象)、字节序列中加载⼀个Bitmap对象。
之前讲的图⽚的三个来源,都可以找到对应的decodeXXXX⽅法来获取该图⽚对应的Bitmap对象。
举个例⼦,假设需要通过⽹络请求⼀张图⽚资源并展⽰:先处理该⽹络请求并得到返回结果的输⼊流对象;依据该流对象调⽤decodeStream⽅法得到⼀个Bitmap对象,该对象即表⽰这张图⽚的内容;最后通过ImageView的setImageBitmap()⽅法显⽰该图⽚即可。
2、Bitmap的压缩存储⽅法
Bitmap的压缩存储与Bitmap的加载是相反的过程,通过compress()⽅法来实现,该⽅法原型为:
compress(pressFormat format, int quality, OutputStream stream)
format参数表⽰压缩存储的格式,可选为PNG、JPEG和WEBP;quality表⽰压缩率,取值在0~100之间,100表⽰未压缩,30表⽰压缩为原⼤⼩的30%,但是该参数在format值为PNG时⽆效,因为PNG属于⽆损压缩⽆法设置压缩率;stream就是希望输出到某个位置的输出流对象,⽐如某个⽂件的输出流对象。
通过compress()⽅法可以将Bitmap按照指定的格式和压缩率(⾮PNG格式时)压缩存储到指定的位置。
3、BitmapFactory.Options类
BitmapFactory是Bitmap的⼯⼚类,通过BitmapFactory的静态⽅法来创建Bitmap对象;BitmapFactory.Options类代表对Bitmap对象的属性设置(配置)。
⼀般情况下,我们调⽤decodeXXXX⽅法时不需要传递⼀个BitmapFactory.Options对象作为参数,因此此时是利⽤默认的配置信息来创建Bitmap对象。
如果需要对创建的Bitmap对象进⾏⾃定义的配置,那么就需要给decodeXXXX⽅法传递⼀个BitmapFactory.Options对象,该对象包含了对Bitmap对象的配置信息。
通过BitmapFactory.Options类的构造器创建BitmapFactory.Options对象。
该对象包含的Bitmap配置信息即为该对象的各属性值,主要有:
介绍⼀下⽐较不好理解的属性:
①inJustDecodeBounds:这个属性表⽰是否只扫描轮廓,默认为false。
如果该属性为true,decodeXXXX⽅法不会返回⼀个Bitmap对象(即不会为Bitmap分配内存)⽽是返回null。
那如果decodeXXXX⽅法不再分配内存以创建⼀个Bitmap对象,那么还有什么⽤呢?答案就是:扫描轮廓。
BitmapFactory.Options对象的outWidth和outHeight属性分别代表Bitmap对象的宽和⾼,但是这两个属性在Bitmap对象未创建之前显然默认为0,默认只有在Bitmap对象创建后才能被赋予正确的值。
⽽当inJustDecodeBounds属性为true,虽然不会分配内存创建Bitmap对象,但是会扫描轮廓来给outWidth和outHeight属性赋值,就相当于绕过了Bitmap对象创建的这⼀步提前获取到Bitmap对象的宽⾼值。
那这个属性到底有啥⽤呢?具体⽤处体现在Bitmap的采样率计算中,后⾯会详细介绍。
②inSample:这个表⽰Bitmap的采样率,默认为1。
⽐如说有⼀张图⽚是2048像素X1024像素,那么默认情况下该图⽚加载到内存中的Bitmap对象尺⼨也是2048像素X1024像素。
如果采⽤的是ARGB_8888⽅式,那么该Bitmap对象加载所消耗的内存为
2048X1024X4/1024/1024=8M。
这只是⼀张图⽚消耗的内存,如果当前活动需要加载⼏张甚⾄⼏⼗张图⽚,那么会导致严重的OOM错误。
OOM错误:尽管Android设备内存⼤⼩可能达到好⼏个G(⽐如4G),但是Andorid中每个应⽤其运⾏内存都有⼀个阈值,超过这个阈值就会引发out of memory即OOM错误(内存溢出错误)。
因为现在市场上流⾏的⼿机设备其操作系统都是在Andori原⽣操作系统基础上的拓展,所以不同的设备环境中这个内存阈值不⼀样。
可以通过以下⽅法获取到当前应⽤所分配的内存阈值⼤⼩,单位为字节:
Runtime.getRuntime().maxMemory();
尽管我们确实可以通过设置来修改这个阈值⼤⼩以提⾼应⽤的最⼤分配内存(具体⽅式是在在Manifest中设置rgeHeap="true"),但是需要注意的是:内存是⼀种很宝贵的资源,不加考虑地⽆脑给每个应⽤提⾼最⼤分配内存是⼀个糟糕的选择。
因为⼿机总内存相⽐较每个应⽤默认的最⼤分配内存虽然⾼很多,但是⼿机中的应⽤数量是⾮常多的,每个应⽤都修改其运⾏内存阈值为⼏百MB甚⾄⼀个G,这很严重影响⼿机性能!另外,如果应⽤的最⼤分配内存很⾼,这意味着其垃圾回收⼯作也会变得更加耗时,这也会影响应⽤和⼿机的性能。
所以,这个⽅案需要慎重考虑不能滥⽤。
关于这个⽅案的理解可以参考⼀位⼤神的解释:“在⼀些特殊的情景下,你可以通过在manifest的application标签下添加largeHeap=true的属性来为应⽤声明⼀个更⼤的heap空间。
然后,你可以通过getLargeMemoryClass()来获取到这个更⼤的heap size阈值。
然⽽,声明得到更⼤Heap阈值的本意是为了⼀⼩部分会消耗⼤量RAM的应⽤(例如⼀个⼤图⽚的编辑应⽤)。
不要轻易的因为你需要使⽤更多的内存⽽去请求⼀个⼤的Heap Size。
只有当你清楚的知道哪⾥会使⽤⼤量的内存并且知道为什么这些内存必须被保留时才去使⽤large heap。
因此请谨慎使⽤large heap属性。
使⽤额外的内存空间会影响系统整体的⽤户体验,并且会使得每次gc的运⾏时间更长。
在任务切换时,系统的性能会⼤打折扣。
另外, large heap并不⼀定能够获取到更⼤的heap。
在某些有严格限制的机器上,large heap的⼤⼩和通常的heap size是⼀样的。
因此即使你申请了large heap,你还是应该通过执⾏getMemoryClass()来检查实际获取到的heap⼤⼩。
”
综上,我们已经知道了Bitmap的加载是⼀个很耗内存的操作,特别是在⼤位图的情况下。
这很容易引发OOM错误,⽽我们⼜不能轻易地通过修改或提供应⽤的内存阈值来避免这个错误。
那么我们该怎么做呢?答案就是:利⽤这⾥所说的采样率属性来创建⼀个原Bitmap的⼦采样版本。
这也是官⽅推荐的对于⼤位图加载的OOM问题的解决⽅案。
其具体思想为:⽐如还是那张尺⼨为2048像素X1024像素图⽚,在inSample值默认为1的情况下,我们现在已经知道它加载到内存中默认是⼀个2048像素X1024像素⼤位图了。
我们可以将inSample设置为2,那么该图⽚加载到内存中的位图宽⾼都会变成原宽⾼的1/2,即1024像素X512像素。
进⼀步,如果inSample值设置为4,那么位图尺⼨会变成512像素X256像素,这个时候该位图所消耗的内存(假设还是ARGB_8888⽅式)为512X256X4/1024/1024=0.5M,可以看出从8M到0.5M,这极⼤的节省了内存资源从⽽避免了OOM错误。
切记:官⽅对于inSample值的要求是,必须为2的幂,⽐如2、4、8...等整数值。
这⾥会有两个疑问:第⼀:通过设置inSample属性值来创建⼀个原⼤位图的⼦采样版本的⽅式来降低内存消耗,听不上确实很不错。
但是这不会导致图⽚严重失真吗?毕竟你丢失了那么多像素点,这意味着你丢失了很多颜⾊信息。
对这个疑问的解释是:尽管在采样的过程确实会丢失很多像素点,但是原位图的尺⼨也在减⼩,其像素密度是不变的。
⽐如说如果inSample值为2,那么⼦采样版本的像素点数量是原来的1/4,但是⼦采样版本的显⽰尺⼨(区域⾯积)也会变成原来的1/4,这样的话像素密码是不变的因此图⽚不⽤担⼼严重失真问题。
第⼆:inSample值如何选取才是最佳?这其实取决于ImageView的尺⼨,具体采样率的计算⽅式后⾯会详细介绍。