BitmapFactory类提供了四类方法用来加载Bitmap:
1、decodeFile(...)
通过图片路径加载,同时可以选择是否设置options,不设置则采用默认options。
例子:
Bitmap bm = BitmapFactory.decodeFile(sd_path)
采用默认options
Bitmap bm = BitmapFactory.decodeFile(sd_path,options)
2、decodeResource(...)
通过传入Resource对象和R.drawable.xxx
形式加载。
Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.aaa);
默认options
3、decodeStream(...)
通过输入流加载
Bitmap bm = BitmapFactory.decodeStream(stream)
,这是一个耗时操作,要在子线程中执行
4、decodeByteArray(...)
从字节数组中加载。通过讲输入流inputstream转换成byte[]字节数组加载。
Bitmap bm = BitmapFactory.decodeByteArray(myByte,0,myByte.length);
** 注:**decodeFile和decodeResource间接调用decodeStream方法。
如果图片过大,直接通过
BitmapFactory
加载,容易出现内存溢出。这样就需要采取一定策略来加载所需的图片。主要就是通过BitmapFactory
内部的一个内部类Options
来实现。
尺寸压缩 是压缩图片的像素,一张图片所占内存的大小 图片类型*宽*高,通过改变三个值减小图片所占的内存,防止OOM,当然这种方式可能会使图片失真 。这是必然的取舍。
设置Options,主要是设置图片色彩模式,采样率来实现。
Android图片色彩模式分类:
Bitmap.Config.ALPHA_8*
:每个像素占用1byte内存。颜色信息只由透明度组成,占8位。
Bitmap.Config.ARGB_4444
:每个像素占用2byte内存。颜色信息由透明度与R(Red),G(Green),B(Blue)四部分组成,每个部分都占4位,总共占16位。
Bitmap.Config.ARGB_8888
:每个像素占用4byte内存。颜色信息由透明度与R(Red),G(Green),B(Blue)四部分组成,每个部分都占8位,总共占32位。是Bitmap默认的颜色配置信息,也是最占空间的一种配置。
Bitmap.Config.RGB_565
:每个像素占用2byte内存。颜色信息由R(Red),G(Green),B(Blue)三部分组成,R占5位,G占6位,B占5位,总共占16位。Android默认的色彩模式为ARGB_8888,这个色彩模式色彩最细腻,显示质量最高。但同样的,占用的内存也最大。
BitmapFactory.Options
的inPreferredConfig
参数可以 指定decode到内存中,手机中所采用的编码,可选值定义在Bitmap.Config
中。缺省值是ARGB_8888。
采样率:
inSampleSize的值必须大于1时才会有效果,且采样率同时作用于宽和高;当inSampleSize=1时,采样后的图片为图片的原始大小。当inSampleSize=2时,采样后的图片的宽高均为原始图片宽高的1/2,这时像素为原始图片的1/(2x2),占用内存也为原始图片的1/(2x2);inSampleSize的取值应该总为2的整数倍,否则会向下取整,取一个最接近2的整数倍,比如inSampleSize=3时,系统会取inSampleSize=2。
假设一张1024x1024,模式为ARGB_8888的图片,inSampleSize=2,原始占用内存大小是4MB,采样后的图片占用内存大小就是(1024/2) x (1024/2 )x4 = 1MB。
public void decodeResource(View view) {
Bitmap bm = decodeBitmapFromResource();
imageview.setImageBitmap(bm);
}
private Bitmap decodeBitmapFromResource(){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.drawable.bbbb, options);
options.inSampleSize = calculateSampleSize(options,300,300);
options.inJustDecodeBounds =false;
return BitmapFactory.decodeResource(getResources(),R.drawable.bbbb,options);
}
// 计算合适的采样率(当然这里还可以自己定义计算规则),reqWidth为期望的图片大小,单位是px
private int calculateSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){
Log.i("========","calculateSampleSize reqWidth:"+reqWidth+",reqHeight:"+reqHeight);
int width = options.outWidth;
int height =options.outHeight;
Log.i("========","calculateSampleSize width:"+width+",height:"+height);
int inSampleSize = 1;
int halfWidth = width/2;
int halfHeight = height/2;
while((halfWidth/inSampleSize)>=reqWidth&& (halfHeight/inSampleSize)>=reqHeight){
inSampleSize*=2;
Log.i("========","calculateSampleSize inSampleSize:"+inSampleSize);
}
return inSampleSize;
}
优点: 效率较高,解析速度快
缺点:采样率inSampleSize的取值只能是2的次方数(例如:inSampleSize=15,实际取值为8;inSampleSize=17,实际取值为16;实际取值会往2的次方结算),因此该方法不能精确的指定图片的大小
1、不用的bitmap即使释放
if (!bmp.isRecycled()) {
bmp.recycle(); //回收图片所占的内存
bitmap = null;
system.gc(); //提醒系统及时回收
}
2、捕获OutOfMemoryError
bitmap在实例化的过程中是很耗内存的。很容易出现OutOfMemery内存溢出的情况。而且一出现程序就会crash。所以,需要对bitmap的实例化的时做OutOfMemoryError捕获,需要注意的是OutOfMemoryError并不是异常而是错误。一般情况下java中异常是可以捕获的。而错误是不可以的,因为Error的出现一般情况下程序就会终止。OutOfMemoryError比较特殊。
Bitmap bitmap = null;
try {
// 实例化Bitmap
bitmap = BitmapFactory.decodeFile(path);
} catch (OutOfMemoryError e) {
> // 如果实例化失败 返回默认的Bitmap对象
return defaultBitmapMap;
}
3、缓存通用的bitmap对象
在加载用户头像的时候,如果用户没有上传的头像,一般会加载一个默认头像。而用户的默认头像又是一样的,所以,对于相同头像的bitmap应该做缓存处理。
如果不进行缓存,尽管看到的是同一张图片文件,但是使用BitmapFactory类的方法来实例化出来的Bitmap,是不同的Bitmap对象。缓存可以避免新建多个Bitmap对象,避免内存的浪费。
4、图片质量压缩
上述用inSampleSize压缩是尺寸压缩,Android中还有一种压缩方式叫质量压缩。质量压缩是在保持像素的前提下改变图片的位深及透明度等,来达到压缩图片的目的,经过它压缩的图片文件大小(kb)会有改变,但是导入成bitmap后占得内存是不变的,宽高也不会改变。因为要保持像素不变,所以它就无法无限压缩,到达一个值之后就不会继续变小了。显然这个方法并不适用与缩略图,其实也不适用于想通过压缩图片减少内存的适用,仅仅适用于想在保证图片质量的同时减少文件大小的情况而已
。
private void compressImage(Bitmap image, int reqSize) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 质量压缩方法,这里100表示不压缩,
image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
int options = 100;
>
// 循环判断压缩后的图片是否大于reqSize,大于则继续压缩
while (baos.toByteArray().length / 1024 > reqSize) {
baos.reset();//清空baos
// 这里压缩options,把压缩后的数据放到baos中
image.compress(Bitmap.CompressFormat.JPEG, options, baos);
options -= 10;
}
// 把压缩后的baos放到ByteArrayInputStream中
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
//decode图片
Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null); }
①、在加载图片的时候,尽量不要直接使用
setImageBitmap
或setImageResource
或BitmapFactory.decodeResource
来设置一张大图,因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存,可以通过BitmapFactory.decodeStream
方法,创建出一个bitmap,再将其设为ImageView的 source
③、运用Java软引用,进行图片缓存,将需要经常加载的图片放进缓存里,避免反复加载