Android大尺寸图片加载,有效避免OOM
图片来源:
1.从网络加载
2.从文件读取
3.从资源文件加载
这三种情况我们一般使用BitmapFactory的decodeStream,decodeFile,decodeResource来获取Bitmap,最终使用ImageView的setImageBitmap函数来显示。
OOM原因:
BitmapFactory的每个decode函数都会生成一个bitmap对象,用于存放解码后的图像,如果图像数据较大就会造成bitmap对象申请的内存较多,如果图像过多就会造成内存不够用自然就会出现out of memory的现象。
处理图片加载的方法,以BitmapFactory.decodeFile为例。
1 public static Bitmap getFitSampleBitmap(String file_path, int width, int height) { 2 BitmapFactory.Options options = new BitmapFactory.Options(); 3 options.inJustDecodeBounds = true; 4 BitmapFactory.decodeFile(file_path, options); 5 options.inSampleSize = getFitInSampleSize(width, height, options); 6 options.inJustDecodeBounds = false; 7 return BitmapFactory.decodeFile(file_path, options); 8 } 9 public static int getFitInSampleSize(int reqWidth, int reqHeight, BitmapFactory.Options options) { 10 int inSampleSize = 1; 11 if (options.outWidth > reqWidth || options.outHeight > reqHeight) { 12 int widthRatio = Math.round((float) options.outWidth / (float) reqWidth); 13 int heightRatio = Math.round((float) options.outHeight / (float) reqHeight); 14 inSampleSize = Math.min(widthRatio, heightRatio); 15 } 16 return inSampleSize; 17 }
BitmapFactory提供了BitmapFactory.Option,用于设置图像相关的参数,主要介绍option里的两个成员:inJustDecodeBounds(Boolean类型) 和inSampleSize(int类型)。
inJustDecodeBounds :如果设置为true则表示decode函数不会生成bitmap对象,仅是将图像相关的参数填充到option对象里,这样我们就可以在不生成bitmap而获取到图像的相关参数了。
inSampleSize:表示对图像像素的缩放比例。假设值为2,表示decode后的图像的像素为原图像的1/2。在上面的代码里我们封装了个简单的getFitInSampleSize函(将传入的option.outWidth和option.outHeight与控件的width和height对应相除再取其中较小的值)来获取一个适当的inSampleSize。
在设置了option的inSampleSize后我们将inJustDecodeBounds设置为false再次调用decode函数时就能生成bitmap了。
1 public class BitmapUtils { 2 3 //decodeFile 4 public static Bitmap getFitSampleBitmap(String file_path, int width, int height) { 5 BitmapFactory.Options options = new BitmapFactory.Options(); 6 options.inJustDecodeBounds = true; 7 BitmapFactory.decodeFile(file_path, options); 8 options.inSampleSize = getFitInSampleSize(width, height, options); 9 options.inJustDecodeBounds = false; 10 return BitmapFactory.decodeFile(file_path, options); 11 } 12 13 //decodeResource 14 public static Bitmap getFitSampleBitmap(Resources resources, int resId, int width, int height) { 15 BitmapFactory.Options options = new BitmapFactory.Options(); 16 options.inJustDecodeBounds = true; 17 BitmapFactory.decodeResource(resources, resId, options); 18 options.inSampleSize = getFitInSampleSize(width, height, options); 19 options.inJustDecodeBounds = false; 20 return BitmapFactory.decodeResource(resources, resId, options); 21 } 22 23 //decodeStream 24 public static Bitmap getFitSampleBitmap(InputStream inputStream, int width, int height) throws Exception { 25 BitmapFactory.Options options = new BitmapFactory.Options(); 26 options.inJustDecodeBounds = true; 27 byte[] bytes = readStream(inputStream); 28 //BitmapFactory.decodeStream(inputStream, null, options); 29 BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options); 30 options.inSampleSize = getFitInSampleSize(width, height, options); 31 options.inJustDecodeBounds = false; 32 // return BitmapFactory.decodeStream(inputStream, null, options); 33 return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options); 34 } 35 36 /* 37 * 从inputStream中获取字节流 数组大小 38 * */ 39 public static byte[] readStream(InputStream inStream) throws Exception { 40 ByteArrayOutputStream outStream = new ByteArrayOutputStream(); 41 byte[] buffer = new byte[1024]; 42 int len = 0; 43 while ((len = inStream.read(buffer)) != -1) { 44 outStream.write(buffer, 0, len); 45 } 46 outStream.close(); 47 inStream.close(); 48 return outStream.toByteArray(); 49 } 50 public static int getFitInSampleSize(int reqWidth, int reqHeight, BitmapFactory.Options options) { 51 int inSampleSize = 1; 52 if (options.outWidth > reqWidth || options.outHeight > reqHeight) { 53 int widthRatio = Math.round((float) options.outWidth / (float) reqWidth); 54 int heightRatio = Math.round((float) options.outHeight / (float) reqHeight); 55 inSampleSize = Math.min(widthRatio, heightRatio); 56 } 57 return inSampleSize; 58 } 59 }
在处理stream的时候我们并不是同之前一样通过调用两次decodeStream函数来进行设置的,而是将stream转化成byte[],然后在两次调用decodeByteArray。其原因是:如果我们两次调用按照两次调用decodeStream的方式,会发现我们得到到bitmap为null。