Android在加载大背景图或者大量图片时,经常导致内存溢出(Out of Memory Error),解决方案如下:
一,3种在代码中设置图片方式有区别,最好不使用setImageResource,原因:
(1)setImageResource的参数是resId,必须是drawable目录下的资源.在UI线程中对图片读取和解析的,所以有可能对一个Activity的启动造成延迟。
(2)setImageDrawable参数是Drawable,也是可以接受不同来源的图片,方法中所做的事情就是更新ImageView的图片。
(3)setImageBitmap参数是Bitmap,把Bitmap对象封装成Drawable对象,然后调用setImageDrawable来设置图片。
二,BitmapFactory提供了几种解码方式(decodeByteArray(), decodeFile(), decodeResource(),decodeStream()等等),以便从多种资源中创建一个Bitmap(位图)对象。
建议使用decodeStream(),最节省空间,因为它直接调用JNI>>nativeDecodeAsset()来完成decode,无需再使用java层的createBitmap。
/**
* 对一张大图片进行适当的压缩,让它能够以最佳大小显示的同时,还能防止OOM的出现。
* @param path SD卡中的图片的路径
* @param reqWidth reqHeight 压缩图片的尺寸,单位是像素(sp)
*/
public static Bitmap getLimitSizeBitmapFromPath(String path, int reqWidth, int reqHeight)
throws IOException{
//1.加载位图
FileInputStream is = new FileInputStream(path);
BitmapFactory.Options opts=new BitmapFactory.Options();
//2.设置位图缩放比例
opts.inJustDecodeBounds = true;
// 当inJustDecodeBounds = true时,decodeFile禁止为bitmap分配内存,返回值也不再是一个Bitmap对象,而是null
// 但是BitmapFactory.Options的outWidth、outHeight和outMimeType属性都会被赋值
Bitmap bitmap = BitmapFactory.decodeFile(path, opts);
final int height = opts.outHeight;
final int width = opts.outWidth;
int scale = 1;
if (height > reqHeight || width > reqWidth) {
// 计算出实际宽高和目标宽高的比率
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高
// 一定都会大于等于目标的宽和高。
scale = heightRatio < widthRatio ? heightRatio : widthRatio;
}
opts.inSampleSize = scale;
opts.inJustDecodeBounds = false;
//3.设置位图颜色显示优化方式
//ALPHA_8:每个像素占用1byte内存(8位)
//ARGB_4444:每个像素占用2byte内存(16位)
//ARGB_8888:每个像素占用4byte内存(32位),Android默认的颜色模式,占用的内存也最大
//RGB_565:每个像素占用2byte内存(16位)
opts.inPreferredConfig = Bitmap.Config.RGB_565;
//4.设置图片可以被回收,创建Bitmap用于存储Pixel的内存空间在系统内存不足时可以被回收
opts.inPurgeable = true;
//5.设置解码位图的尺寸信息
opts.inInputShareable = true;
//6.解码位图:decodeStream是最节省空间的一种方法
bitmap = BitmapFactory.decodeStream(is, null, opts);
is.close();
return bitmap;
}
/**
* 对一张大图片进行适当的压缩,让它能够以最佳大小显示的同时,还能防止OOM的出现。
* @param path SD卡中的图片的路径
* @param reqWidth reqHeight 压缩图片的尺寸,单位是像素(sp)
*/
public static Bitmap getLimitSizeBitmapFromPath(String path, int reqWidth, int reqHeight)
throws IOException{
//1.加载位图
FileInputStream is = new FileInputStream(path);
BitmapFactory.Options opts=new BitmapFactory.Options();
//2.设置位图缩放比例
opts.inJustDecodeBounds = true;
// 当inJustDecodeBounds = true时,decodeFile禁止为bitmap分配内存,返回值也不再是一个Bitmap对象,而是null
// 但是BitmapFactory.Options的outWidth、outHeight和outMimeType属性都会被赋值
Bitmap bitmap = BitmapFactory.decodeFile(path, opts);
final int height = opts.outHeight;
final int width = opts.outWidth;
int scale = 1;
if (height > reqHeight || width > reqWidth) {
// 计算出实际宽高和目标宽高的比率
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高
// 一定都会大于等于目标的宽和高。
scale = heightRatio < widthRatio ? heightRatio : widthRatio;
}
opts.inSampleSize = scale;
opts.inJustDecodeBounds = false;
//3.设置位图颜色显示优化方式
//ALPHA_8:每个像素占用1byte内存(8位)
//ARGB_4444:每个像素占用2byte内存(16位)
//ARGB_8888:每个像素占用4byte内存(32位),Android默认的颜色模式,占用的内存也最大
//RGB_565:每个像素占用2byte内存(16位)
opts.inPreferredConfig = Bitmap.Config.RGB_565;
//4.设置图片可以被回收,创建Bitmap用于存储Pixel的内存空间在系统内存不足时可以被回收
opts.inPurgeable = true;
//5.设置解码位图的尺寸信息
opts.inInputShareable = true;
//6.解码位图:decodeStream是最节省空间的一种方法
bitmap = BitmapFactory.decodeStream(is, null, opts);
is.close();
return bitmap;
}
三,如果使用imageView.setImageBitmap()来加载图片,在Activity或者Fragment在onStop/onDestroy释放图片资源。
if(imageView != null) {
BitmapDrawable b = (BitmapDrawable)v.getDrawable();
if(b != null){
Bitmap oldBitmap = b.getBitmap();
if(oldBitmap != null && !oldBitmap.isRecycled()){
oldBitmap.recycle();
oldBitmap = null;
}
}
}
if(imageView != null) {
BitmapDrawable b = (BitmapDrawable)v.getDrawable();
if(b != null){
Bitmap oldBitmap = b.getBitmap();
if(oldBitmap != null && !oldBitmap.isRecycled()){
oldBitmap.recycle();
oldBitmap = null;
}
}
}
四,如果使用imageView.setImageDrawable()来加载图片,在Activity或者Fragment在onStop/onDestroy释放图片资源。
if(imageView != null) {
Drawable d = (ImageView).getDrawable();
if (d != null) {
//清除setImageDrawable(d)对d的引用
d.setCallback(null);
}
ImageView.setImageDrawable(null);
}
if(imageView != null) {
Drawable d = (ImageView).getDrawable();
if (d != null) {
//清除setImageDrawable(d)对d的引用
d.setCallback(null);
}
ImageView.setImageDrawable(null);
}