-
ARGB_8888:表示32位ARGB位图,各占8位,一个像素点占8*4=32位,即4字节
-
RGB_565:表示16位RGB位图,R5位,G6位,B5位,没有透明度,一个像素占5+6+5=16位即2字节
我们一般使用ARGB_8888格式来存储Bitmap,而ARGB_4444画质比较惨不忍睹,所以在API 13中已经被弃用了。
假如对图片没有透明度要求,则可以改成RGB_565的格式,相比ARGB_节省一半的内存开销。
注意一个概念:内存中存储的Bitmap对象与文件中存储的Bitmap图片不是要一个概念。文件中存储的Bitmap图片是经过我们在后面讲到的压缩算法压缩过的;而内存中存储的Bitmap对象是通过BitmapFactory或者Bitmap的Create方法创建的,它保存在内存中,而且具有明确的宽和高,所以,很明显,内存中存储的一个Bitmap对象,它所占的内存大小 = bitmap宽* 高 *每个像素所占内存大小。
2、创建Bitmap的方式之一:BitmapFactory
BitmapFactory用于从各种资源、文件、数据流和字节流中创建Bitmap对象。
BitmapFactory类是一个工具类,提供了大量的函数,这些函数可以用于从不同的数据源中解析、创建Bitmap对象。
这些函数网上都罗列出来了,我也懒得敲了,反正就可以根据不同的方式来解析对应的Bitmap对象。
大概就是:
- decodeResource(Resources res,int id)
见得多了
- decodeFile(String pathName)
根据路径名来加载图片。必须是全路径名 比如 "/data/data/demo.jpg"这样
- decodeByteArray(byte[] data,int offset ,int length)
data:压缩图像数据的字节数组
offset:图像数据偏移量,用于解码器定位从哪里开始解析
length:字节数,从偏移量开始,指定取多少字节进行解析。
一般是从网络上下载一个InputSteam的流,然后转换成byte,再将其转换成bitmap
- decodeFileDescriptior(FileDescriptior fd)
通过FileDescriptor对象解析出对应的Bitmap,而FD的一般获取方式是通过构造FileInputStream对象。而FileInputStream又是通过Path拿到的,那为什么我们不直接使用decodeFile直接解析呢?是因为该函数比decodeFile函数更节省内存。
- decodeStream(InputStream in)
直接传入inputstream就能解析了
3、BitmapFactory.Option
之前在《Android开发艺术探索》中对Option有了印象比较深刻的理解,因为这个参数非常大的影响了图片的存储性能,是降低OOM发生概率的一个比较关键的参数,也是实战、面试中一定会碰到的问题。
但之前理解过很多,所以这里只写之前没有遇到过的。
下面是Options常用的部分成员变量:
public boolean inJustDecodeBounds;
public int inSampleSize;
public int inDensity;
public int inTargetDensity;
public int inScreenDensity;
public boolean inScaled;
public Bitmap.Config inPreferredConfig;
public int outWidth;
public int outHeight;
public String outMimeTye;
其中以in开头的就是设置某某参数,以out开头的就是获取某某参数,比如outWidth就是获取Bitmap的宽。
(1)inJustDecodeBounds 获取图片信息
如果将这个字段设置为true,则表示只解析图片信息,不获取图片、不分配内存,能获取的信息有图片的宽高、MIME。宽高通过outWidth outHeight返回,MIME通过outMimeType返回。
我们在压缩图片时就会经常使用这个字段。一般而言,图片过大时,经常会造成OOM。所以,当图片的尺寸大于我们想要的尺寸时,我们就要进行压缩。这个问题的关键在于,如何不将图片加载到内存中,依然可以知道它的尺寸?而这个参数就很好的完成了这个需求。
我们只需将inJustDecodeBounds设置为true,而不需要将图片加载到内存中,就可以得知它的宽高,然后跟我们想要的尺寸进行对比,进行压缩。
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.avator_xizuka, options);
Log.d(TAG, “bitmap:” + bitmap);
Log.d(TAG, “realwidth:” + options.outWidth + " realheight:" + options.outHeight
- " mimeType:" + options.outMimeType);
可以得出下面打印信息:
看到bitmap为null,但是realwidth和realheight和mimeType都是有值的,这说明没有获取到图片,只是解析了它。
解析是不会占用任何内存的。
(2)inSampleSize
就是采样率,balabalabala
这里有一点,比如ImageView是100_100 而图片像素时300_400 这个时候我们要图片缩小3倍还是4倍呢
因为inSampleSize本来就是失真处理,所以尽量让失真效果没那么强,这里就是缩小3倍的。(举个例子,也不能设inSampleSize为3的)
(3)加载一个Bitmap文件究竟需要占多少空间
之前说过 Bitmap所占的内存是 “宽 * 高 * 4” (因为Bitmap现在都默认ARGB_8888了所以为4字节)
但是这个公式是不是就是一定适用于所有Android机 所有情况呢?
其实不是。根本原因是屏幕的适配。
在Android最初版本的时候,设计人员就考虑到了以后Android以后会有许许多多的不同分辨率的屏幕,所以就定制了几个比如drawable-ldpi drawable-mdpi。。。资源文件,当图片符合该分辨率,就不用进行拉伸或者压缩,但是dpi不止那么几个啊,市面上的不同dpi手机太多了,肯定有图片和该这些分辨率文件不一致,所以,Android就会对该Bitmap进行动态的拉伸/压缩。
比如一张图片在SD卡下的原图是600_800 在Activity使用加载该图片后的内存是2MB,然后将它放入到Drawable-xhdpi下,发现它的宽高被拉伸到了960_1200 这个时候通过Activity加载图片就是4MB多了。
总结:
-
不同名称的资源文件是为了适配不同的分辨率,当屏幕分辨率与文件所在资源文件加对应的分辨率相同时,会直接使用图片,否则会对图片进行缩放。
-
当从本地文件SD卡或者内存中加载图片时,不会对图片进行缩放。
(4)inScaled、inDensity、inTargetDensity、inScreenDensity
- inScaled
只有在一种情况下才会对Bitmap进行拉缩放:就是当图片所在资源文件夹所对应的屏幕分辨率与真实显示的屏幕分辨率不同时。
而这个参数表示,在需要缩放时,是否对当前文件进行缩放。如果inScaled设置为false,则不进行缩放,如果设置为true或者不设置,则会对Bitmap进行动态的缩放。
- inDensity
用于设置文件所在资源文件夹的屏幕分辨率
- inTargetDensity
表示真实显示的屏幕分辨率
- inScreenDensity
真实的分辨率,不过该参数很鸡肋,源码中也没有出现过。就很尴尬。
一张图片的缩放比例在这里就是 scale = inTargetDensity / inDensity
所以这两个参数的作用就是:手动设置文件所在资源文件夹的分辨率和真实显示的屏幕分辨率来指定图片的缩放比例。
(5)inPreferredConfig
用来设置存储格式的。可以设置ARGB_8888、RGB_565。。。
4、创建Bitmap方法之二:静态方法
除了用BitmapFactory.decodeXXX函数来加载图片,还可以通过Bitmap自带的静态方法加载图片
就是 createBitmap(...)
createScaledBitmap(...)
createBitmap 的构造函数很多,但都容易理解,这里就不再讲解。
来看下createScaledBitmap(Bitmap src,int dstWidth,int dstHeight,boolean filter)
其中Bitmap src表示要缩放的源图像,dstWidth dstHeight表示缩放后的目标宽高,filter表示是否给图像添加滤波效果
到这里,有关创建Bitmap的方法就介绍VAN了
总结一下:
-
加载图像可以使用BitmapFactory和Bitmap的相关方法。
-
Options参数很牛逼,要多多运用
-
如果要裁剪或者缩放图片,则只能使用Bitmap的Create系列函数
-
一定要注意,在加载或者创建Bitmap时,必须要使用try…catch语句捕捉OutOfMemoryError,防止出现OOM。
5、常用函数
(1)copy(Config config,boolean isMutable)
这个函数是根据源图像来创建一个副本,但可以指定副本的像素存储格式。它的两个参数含义为:
config表示存储格式
isMutable表示新创建的Bitmap是否可以更改其中元素。
诶~原来图像的像素还是可以被改变的吗?
其实是的,但是之前所学的加载和创建图片的方法不是每种弄出来的图像的像素都是可以改变的。
我们可以通过下面函数来判断图像像素是否可以更改。true表示可以,false表示不可以。
boolean isMutable();
如果图像的该函数返回了false,你还要用setPixel()等函数来更改,诶,就会报错。
通过BitmapFactory创建出来的Bitmap都是像素不可以更改的,只有通过Bitmap中的下面几个函数创建的Bitmap才是像素可更改的。
copy(Bitmap.Config config,boolean isMutable)
createBitmap(int width,int height,Bitmap.Config config)
createSCaledBitmap(Bitmap src,int dstWidth,int dstHeight,boolean filter)
createBitmap(DisplayMetrics display,int width,int height,Bitmap.Config config)
大家谨记,对于像素不可以更改的图像,是不能作为画布的,比如下面这个:
Bitmap bmp = BitmapFactory.decodeResources(getRescoures(),R.drawable.xxx);
Canvas canvas = new Canvas(bmp);
canvas.drawColor(Color.RED);
这个时候,因为Bitmap是由工厂创建出来的,所以像素不可以更改,所以这个时候不能作为画布,这样写就会报错。
(2)extractAlpha()
这个函数的作用是Bitmap中抽出Alpha值,生成一幅只含有Alpha值的图像。像素存储的格式是ALPHA_8,有两个构造函数。
分别是:
Bitmap extractAlpha()
Bitmap extractAlpha(Paint paint,int[] offsetXY)
第二个参数中的Paint是具有MaskFilter效果的Paint对象,一般使用BlurMaskFilter模糊效果。
第二个参数为BlurMaskFilter效果的偏移量。但其取值并不一定与最终BlurMaskFilter的模糊半径一致。它只是一个建议值。
(3)分配控件获取
获取Bitmap的分配空间有三个函数。
//API 19引入,获取Bitmap所分配的内存,API 19以上的机器都使用该函数获取
int getAllocationByteCount();
//获取Bitmap分配的内存,在API 12中引入,在12< API <19 则使用该函数
int getByteCount()
//获取每行所分配的内存大小。Bitmap所占内存 = getRowBytes() * bitmap.getHeight() 即所占内存等于每行所占内存乘以行数。
//这个在API 1中引入,所以API 12一下必须用这个函数
int getRowBytes()
所以一般情况下获取内存分配都是:
if(api > kitkat){
return getAllocationByteCount();
}
if(api > HONEYCOMB_MR1){
return getByteCount();
}
return getRowBytes() * bitmap.getHeight();
(4)recycle()、isRecycled()
这两个与图片回收有关的函数
//强制回收Bitmap所占的内存
public void recycle()
//判断当前Bitmap的内存是否被回收
public final boolean isRecycled()
所以如果要回收内存,要这样写
if(bmp != null && !bmp.isRecycled()){
bmp.recycle();
bmp = null;
system.gc(); //提醒系统及时回收内存
}
注意一:使用内存已经被回收的Bitmap会引起Crash
注意二:是否应该使用recycle()函数主动回收内存
最后
文章所有资料全部已经打包整理好,另外小编手头上整理了大量Android架构师全套学习资料,Android核心高级技术PDF文档+全套高级学习资料+视频+2021 BAT 大厂面试真题解析
资料展示:
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!
; //提醒系统及时回收内存
}
注意一:使用内存已经被回收的Bitmap会引起Crash
注意二:是否应该使用recycle()函数主动回收内存
最后
文章所有资料全部已经打包整理好,另外小编手头上整理了大量Android架构师全套学习资料,Android核心高级技术PDF文档+全套高级学习资料+视频+2021 BAT 大厂面试真题解析
资料展示:
[外链图片转存中…(img-mSadnakF-1714838165639)]
[外链图片转存中…(img-icGdRs8X-1714838165640)]
[外链图片转存中…(img-0pdO1cN7-1714838165640)]
[外链图片转存中…(img-4CePD5Ao-1714838165641)]
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!