有效解决Android加载大图片时内存溢出的问题

 首先解析一下基本的知识: javascript

位图模式,bitmap颜色位数是1位
java

灰度模式,bitmap颜色位数是8位,和256色同样 android

 

RGB模式,bitmap颜色位数是24位 在RGB模式下,一个像素对应的是红、绿、蓝三个字节 算法

CMYK模式,bitmap颜色位数是32位  在CMYK模式下,一个像素对应的是青、品、黄、黑四个字节 app

图像文件的字节数(Byte) = 图像分辨率*颜色深度/8(bit/8) 函数

例如:一幅640*480图像分辨率、RGB色通常为24位真彩色,图像未经压缩的数据容量为:640X480X24/8=921600字节=900KB(1KB=l千字节=1024字节)。
注:一个图像文件占的磁盘空间大小还和磁盘的文件格式有关。如:NTFS最小单位为4KB 因此图像文件大小确定是4KB的倍数。可是有图图片压缩算法的存在,图片文件在保存时,体积要比在内存的大小小得多,如640x480的图片文件大小通常只在200K~300K。这也是为何,加载几MB的图片文件,会致使JVM内存溢出,致使OutofMemoryException的缘由。 google

 

由上面的公式,咱们能够得出,加载的图片所占的内存大小,取决于其分辨率颜色数 spa

 

咱们再来看看Google官方的介绍: .net

 

这个已经很是的明白了,咱们VM的app进程所得到的内存只有区区的16MB,普普统统的5MP摄像头拍出来的图片,直接加载,将占用接近19MB的内存。可见,不进行压缩,内存将会直接溢出。 code

 

再了解一下,android读取解析图片的方式,基本与Java的方式类型,经过文件输入流,而后进行解码,再转成图片格式;

 

固然google的android也为咱们封装好了若干方法,来方便快捷地完成这项工做,如ImageView的setImageBitmap,setImageResource,BitmapFactory的decodeResource等,可是尽可能不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图,由于这些函数在完成decode后,最终都是经过java层的createBitmap来完成的,须要消耗更多内存;

 

所以,改用先经过BitmapFactory.decodeStream方法,建立出一个bitmap,再将其设为ImageView的source,加载显示。decodeStream最大的秘密在于其直接调用JNI>>nativeDecodeAsset()来完成decode,无需再使用java层的createBitmap,从而节省了java层的空间。

 

在使用 decodeStream 读取图片时,再加上Config参数,就能够更有效地控制加载目标的内存大小,从而更有效阻止抛 OutofMemoryException异常,下面用一段代码说明:
Java代码    收藏代码
  1. public static Bitmap readBitmapAutoSize(String filePath, int outWidth, int outHeight) {    
  2.                 //outWidth和outHeight是目标图片的最大宽度和高度,用做限制  
  3.         FileInputStream fs = null;  
  4.         BufferedInputStream bs = null;  
  5.         try {  
  6.             fs = new FileInputStream(filePath);  
  7.             bs = new BufferedInputStream(fs);  
  8.             BitmapFactory.Options options = setBitmapOption(filePath, outWidth, outHeight);  
  9.             return BitmapFactory.decodeStream(bs, null, options);  
  10.         } catch (Exception e) {  
  11.             e.printStackTrace();  
  12.         } finally {  
  13.             try {  
  14.                 bs.close();  
  15.                 fs.close();  
  16.             } catch (Exception e) {  
  17.                 e.printStackTrace();  
  18.             }  
  19.         }  
  20.         return null;  
  21.     }  
  22.   
  23. private static BitmapFactory.Options setBitmapOption(String file, int width, int height) {  
  24.         BitmapFactory.Options opt = new BitmapFactory.Options();  
  25.         opt.inJustDecodeBounds = true;            
  26.                 //设置只是解码图片的边距,此操做目的是度量图片的实际宽度和高度  
  27.         BitmapFactory.decodeFile(file, opt);  
  28.   
  29.         int outWidth = opt.outWidth; //得到图片的实际高和宽  
  30.         int outHeight = opt.outHeight;  
  31.         opt.inDither = false;  
  32.         opt.inPreferredConfig = Bitmap.Config.RGB_565;      
  33.                 //设置加载图片的颜色数为16bit,默认是RGB_8888,表示24bit颜色和透明通道,但通常用不上  
  34.         opt.inSampleSize = 1;                            
  35.                 //设置缩放比,1表示原比例,2表示原来的四分之一....  
  36.                 //计算缩放比  
  37.         if (outWidth != 0 && outHeight != 0 && width != 0 && height != 0) {  
  38.             int sampleSize = (outWidth / width + outHeight / height) / 2;  
  39.             opt.inSampleSize = sampleSize;  
  40.         }  
  41.   
  42.         opt.inJustDecodeBounds = false;//最后把标志复原  
  43.         return opt;  
  44.     }  
另外,decodeStream直接拿的图片来读取字节码了, 不会根据机器的各类分辨率来自动适应, 使用了decodeStream以后,须要在hdpi和mdpi,ldpi中配置相应的图片资源, 不然在不一样分辨率机器上都是一样大小(像素点数量),显示出来的大小就不对了。 可参考下面的代码:

Java代码    收藏代码
  1. BitmapFactory.Options opts = new BitmapFactory.Options();  
  2. //设置图片的DPI为当前手机的屏幕dpi  
  3. opts.inTargetDensity = ctx.getResources().getDisplayMetrics().densityDpi;    
  4. opts.inScaled = true;  

另外,图片的bitmap对象为大对象,不用了要注意主动回收,

Java代码    收藏代码
  1. if(!bmp.isRecycle() ){  
  2.         bmp.recycle()   //回收图片所占的内存  
  3.         system.gc()  //提醒系统及时回收   
 
相关文章
相关标签/搜索