Android学习笔记(一)------Bitmap 位图
背景:
无论任何App的开发都离不开图片的加载,通常图片加载有三种方式:资源项目图片,本地图片和网络图片资源等。通常采用ImageView作为载体,设置ImageView的相应的API来显示图片内容。
例如:通过ImageView的setImageResource(int id)方法来显示资源项目图片内容,但是有一个问题,加入我们使用的图片是来自于本地或者网络资源,又该如何呢?
所有引入第一个问题:如何实现从本地或者网络资源上显示图片?这个时候可能会想到一个更深的问题,那就是图片是以怎样的方式在Android中显示。此时引入第二个问题:图片是以怎样的方式显示出来?
都知道,假设使用TextView来显示本地一个文本内容,只需要将该文件创建并包装成一个输入流对象,通过该输入流对象获取代表文本内容的字符串对象,最后通过TextView的相应API来显示文本内容。但是,如果要加载图片呢?实际上会发现,图片内容无法通过字符串来表示(你可以通过文字描述完整表达出一张图片吗?)。
我们都知道,一个面就由无数个点构成,同样道理,一张图片也是有无数个像素点有序连接构成。即图片的内容就是无数的有序像素点。在此回答第二个问题:实际上实现图片的显示都是通过 “在内存中加载图片内容—>从内存中将内容表达出来” ,这时候会想到,怎么才可以将图片内容在内存中表示出来呢?这个时候回答第一个问题:那就是,Bitmap(位图)。
概念描述:
Bitmap(位图):本质是图片在内存中表达形式。都知道面由无数的点构成,但是对于图片来说,没有必要用无数的像素点进行图片表达,其一无数的像素点信息量太大无法存储,其二由于人类的肉眼能力有限,我们只需要将图片内容以 有限但足够多 的形式表达出来即可,“无限–>有限”,将图片的内容表达成 有限但足够多的像素点的集合,只需要对这些像素点进行存储便可以将整张图片在内存中表达出来。
像素信息:
每一个像素点都包含了图片的信息,但无非就四个ARGB通道。其中A代表的是 透明度,RGB代表的是 红绿蓝 三种颜色通道,每一个通道的取值范围0~255,即256种取值方式,刚好通过一个字节(8bit)进行表达。所以一个通道为一字节,一个像素点以4字节在内存中表示出来。
但是,在现实中,一张图片的就包含了许多的像素点,而同时也存储了大量的图片,这必然带来了存储空间占用过大问题,其次有些时候,我们也不需要A(透明度)值,例如全家福,婚纱照等,除了我们需要将图片设置为背景时候,否者一般情况这个值是毫无愿意的。所以实际上A值也是要根据实际情况来设定的。
总结:Bitmap对象实际就是图片在内存中表达形式,它将图片在内存中表达成 有限足够多的像素点的集合,每一个像素点都包含了所在的像素点信息即ARGB,确定每一个像素点的ARGB值,也就确定好了图片的内容。
所以,Android图片加载无论以哪种方式,都是先将图片的内容加载到内存中,在通过ImageView对应API来展现图片的内容。
Bitmap.comfig:
comfig为Bitmap的枚举内部类,表示了每个像素点ARGB的存储方案:
ARGB_8888:表示每个通道为8bit,每个像素点4字节,此方案存储图盘质量最高,但占用空间大。
ARGB_4444:表示每个通道4bit,每个像素点2字节,占用空间小,但图片质量低。
ARGB_565:没有A值通道存储,RGB通道分别占5.6.5位。不支持透明值,每个像素点2字节。
ALPHA_8:不支持颜色通道,只支持透明值,用途特殊。
Bitmap的压缩存储:
Bitmap是在内存中表达方式,如果要Bitmap对象的图片内容进行持久化存储为一张图片,则根据不同的压缩算法可以得到不同的图片压缩格式,如JPEG,PNG,GIF等。也就是说,Bitmap是在内存中的表示,JPEG,PNG,GIF等格式图片是持久化存储后的图片。
1.无损压缩:通过扫描图片中重复类容,将重复的内容删除只需要记录好重复区域的起点,无损压缩可以减少压缩后的空间占用,当加载到内存中的时候,重复的区域也会被加载进来,所以无法减少在内存的资源占用。
2.有损压缩:通过把图片中的边缘颜色删除,即使加载到内存中被删除的数据也不会加载进来,减少了内存占用,同时也可以根据情况设置压缩率,但是带来的问题就是图片的内容相对无损压缩会偏低,尤其在高压缩率的情况下。
Bitmap的简单运用:
Bitmap的BitmapFactory(工厂类)提供了四种方法加载图片资源:decodeFile、decodeResource、decodeStream、decodeByteArray。分别是本地图片加载,资源项目图片加载,串流图片加载和字节序列图片加载。举一个例子:例如要从网络上加载图片,需用通过网络请求获得数据串流(输入流对象),依据该流对象使用decodeStream获取一个Bitmap对象,最后通过ImageView的setImageBitmap(Bitmap b)从内存中显示出来。
Bitmap的压缩存储方法:
和加载图片相反,通过compress()方法实现Bitmap的压缩存储,其实质是:
compress(Bitmap.CompressFormat format, int quality, OutputStream stream)
其中,format对象代表的是 压缩后的格式,可以选择PNG,JPEG等。quality对象代表的是 压缩率,其取值范围0~100,100代表无压缩,10代表压缩为原来的10%。stream对象代表的是 某个位置的输出流对象。
注意:当设置PNG格式时候无法设置压缩率,原因是PNG属于无损压缩格式。
BitmapFactory.Option类:
BitmapFactory.Option类为Bitmap各个属性设置,当自定义一个Bitmap对象时候,decodeXXX()方法就需要传递一个BitmapFactory.Option对象。
通过BitmapFactory.Option类的构造方法创建出BitmapFactory.Option类对象,该对象包含了Bitmap的各属性的配置信息,如下:
boolean inJustDecodeBounds: 是否只扫描轮廓。
int inSample: 采样率。
** Bitmap.Config inPreferredConfig:** 格式,色彩模式
int outWidth: bitmap对象的宽。
int outHeight: bitmap对象的高。
boolean inDither: 防抖动,默认为false。
boolean inScaled: 是否可以缩放,默认为true。
boolean inMutable: 是否可变,当设为true时,decode转换的方法返回结果全部可以改变。
其中:
一。inJustDecodeBounds属性,当设置为true时,decodeXXX()方法不会分配内存来创建Bitmap对象,会返回一个null;outWidth和outHeight这两个属性只有Bitmap对象创建时才会有值,但如果这时候inJustDecodeBounds属性设置为true,虽然此时不分配内存以创建Bitmap对象,但是扫描轮廓会绕过Bitmap对象的创建来获取Bitmap对象的宽高值。具体的体现在Bitmap的采样率计算。
二。inSample:代表了Bitmap的采样率,默认为1,即一张图片是1920x1080的像素,在默认情况下,加载内存到也是为1920x1080像素,即使采用了ARGB_8888压缩方案,其在内存的大小为192010804/1024/1024约为8M,这只是一张图在内存中的大小,如果当前活动需要加载几十张图片的话,会带来严重的OOM(Out Of memory:即内存溢出)问题。
OOM:虽然当前流行Android设备大小可能达到好几个G,但是在Android中每一个运用其运行内存占用有一个阈值,当超出这个阈值时候,就会发生OOM,可以通过Runtime.getRuntime().maxMemory();来获取当前应用的内存阈值大小。
尽管我们可以通过设置来修改这个运用的阈值,但是要明白,内存属于一种宝贵的资源,不加考虑的给每一个应用提高最大内存阈值是不妥当的,这回带来严重的手机影响影响问题。所以采取提高运用的内存阈值需要慎重考虑才行,不得以滥用。
总言之,Bitmap对象操作属于一种很消耗内存且耗时的操作,尤其在大位图的时候,容易引发OOM错误,但是也有处理方案,那就是利用采样率属性来创建原Bitmap的子采样版本。例如:原图片为1920x1080像素图片,在inSample默认情况下加载内存属于很耗内存,但是如果将inSample值设置为2的时候,图片的宽高尺寸就会变成原宽高的1/2,像素也会随之降低,即使采用ARGB_8888方案,这个时候消耗的内存也比原先的消耗内存为少。
注意的是,设置inSample属性的值时候,设置的值必须为2的幂,如2,4,8等。。。
Bitmap加载的异步问题:
由于我们都知道Bitmap操作属于耗时耗内存的方式,为了避免ANR(应用无法响应)错误,所以在进行Bitmap操作的时候,需要在工作线程执行,获取到Bitmap对象再然后返回主线程UI进行更新设置。
例如:
知识总结启发,详细的知识总结网址为:
https://www.cnblogs.com/shakinghead/p/11025805.html
最后,写的不好望见谅!