zoukankan      html  css  js  c++  java
  • 彻底解决Android因加载多个大图引起的OutOfMemoryError,内存溢出的问题

    http://blog.csdn.net/wulianghuan/article/details/11548373?reload

    最近因为项目里需求是选择或者拍摄多张照片后,提供滑动预览和上传,很多照片是好几MB一张,因为目前的Android系统对运行的程序都有一定的内存限制,一般是16MB或24MB(视平台而定),不做处理直接加载的话必然会报OOM (Out Of Memmory)。网上有很多解决android加载bitmap内存溢出的方法,我总结了一个通用的方法,下面是我从的开发案例抽取出来的代码:

    我在项目中建了个Util.java工具类,里面写了个方法,根据图片的路径返回一个字节流数组对象:

     

    1. public static byte[] decodeBitmap(String path) {  
    2.         BitmapFactory.Options opts = new BitmapFactory.Options();  
    3.         opts.inJustDecodeBounds = true;// 设置成了true,不占用内存,只获取bitmap宽高   
    4.         BitmapFactory.decodeFile(path, opts);  
    5.         opts.inSampleSize = computeSampleSize(opts, -1, 1024 * 800);  
    6.         opts.inJustDecodeBounds = false;// 这里一定要将其设置回false,因为之前我们将其设置成了true   
    7.         opts.inPurgeable = true;  
    8.         opts.inInputShareable = true;  
    9.         opts.inDither = false;  
    10.         opts.inPurgeable = true;  
    11.         opts.inTempStorage = new byte[16 * 1024];  
    12.         FileInputStream is = null;  
    13.         Bitmap bmp = null;  
    14.         ByteArrayOutputStream baos = null;  
    15.         try {  
    16.             is = new FileInputStream(path);  
    17.             bmp = BitmapFactory.decodeFileDescriptor(is.getFD(), null, opts);  
    18.             double scale = getScaling(opts.outWidth * opts.outHeight,  
    19.                     1024 * 600);  
    20.             Bitmap bmp2 = Bitmap.createScaledBitmap(bmp,  
    21.                     (int) (opts.outWidth * scale),  
    22.                     (int) (opts.outHeight * scale), true);  
    23.             bmp.recycle();  
    24.             baos = new ByteArrayOutputStream();  
    25.             bmp2.compress(Bitmap.CompressFormat.JPEG, 100, baos);  
    26.             bmp2.recycle();  
    27.             return baos.toByteArray();  
    28.         } catch (FileNotFoundException e) {  
    29.             e.printStackTrace();  
    30.         } catch (IOException e) {  
    31.             e.printStackTrace();  
    32.         } finally {  
    33.             try {  
    34.                 is.close();  
    35.                 baos.close();  
    36.             } catch (IOException e) {  
    37.                 e.printStackTrace();  
    38.             }  
    39.             System.gc();  
    40.         }  
    41.         return baos.toByteArray();  
    42.     }  
    43.   
    44.     private static double getScaling(int src, int des) {  
    45.         /** 
    46.          * 48 目标尺寸÷原尺寸 sqrt开方,得出宽高百分比 49 
    47.          */  
    48.         double scale = Math.sqrt((double) des / (double) src);  
    49.         return scale;  
    50.     }  
    51.   
    52.     public static int computeSampleSize(BitmapFactory.Options options,  
    53.             int minSideLength, int maxNumOfPixels) {  
    54.         int initialSize = computeInitialSampleSize(options, minSideLength,  
    55.                 maxNumOfPixels);  
    56.   
    57.         int roundedSize;  
    58.         if (initialSize <= 8) {  
    59.             roundedSize = 1;  
    60.             while (roundedSize < initialSize) {  
    61.                 roundedSize <<= 1;  
    62.             }  
    63.         } else {  
    64.             roundedSize = (initialSize + 7) / 8 * 8;  
    65.         }  
    66.   
    67.         return roundedSize;  
    68.     }  
    69.   
    70.     private static int computeInitialSampleSize(BitmapFactory.Options options,  
    71.             int minSideLength, int maxNumOfPixels) {  
    72.         double w = options.outWidth;  
    73.         double h = options.outHeight;  
    74.   
    75.         int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math  
    76.                 .sqrt(w * h / maxNumOfPixels));  
    77.         int upperBound = (minSideLength == -1) ? 128 : (int) Math.min(  
    78.                 Math.floor(w / minSideLength), Math.floor(h / minSideLength));  
    79.   
    80.         if (upperBound < lowerBound) {  
    81.             return lowerBound;  
    82.         }  
    83.   
    84.         if ((maxNumOfPixels == -1) && (minSideLength == -1)) {  
    85.             return 1;  
    86.         } else if (minSideLength == -1) {  
    87.             return lowerBound;  
    88.         } else {  
    89.             return upperBound;  
    90.         }  
    91.     }  
    public static byte[] decodeBitmap(String path) {
    		BitmapFactory.Options opts = new BitmapFactory.Options();
    		opts.inJustDecodeBounds = true;// 设置成了true,不占用内存,只获取bitmap宽高
    		BitmapFactory.decodeFile(path, opts);
    		opts.inSampleSize = computeSampleSize(opts, -1, 1024 * 800);
    		opts.inJustDecodeBounds = false;// 这里一定要将其设置回false,因为之前我们将其设置成了true
    		opts.inPurgeable = true;
    		opts.inInputShareable = true;
    		opts.inDither = false;
    		opts.inPurgeable = true;
    		opts.inTempStorage = new byte[16 * 1024];
    		FileInputStream is = null;
    		Bitmap bmp = null;
    		ByteArrayOutputStream baos = null;
    		try {
    			is = new FileInputStream(path);
    			bmp = BitmapFactory.decodeFileDescriptor(is.getFD(), null, opts);
    			double scale = getScaling(opts.outWidth * opts.outHeight,
    					1024 * 600);
    			Bitmap bmp2 = Bitmap.createScaledBitmap(bmp,
    					(int) (opts.outWidth * scale),
    					(int) (opts.outHeight * scale), true);
    			bmp.recycle();
    			baos = new ByteArrayOutputStream();
    			bmp2.compress(Bitmap.CompressFormat.JPEG, 100, baos);
    			bmp2.recycle();
    			return baos.toByteArray();
    		} catch (FileNotFoundException e) {
    			e.printStackTrace();
    		} catch (IOException e) {
    			e.printStackTrace();
    		} finally {
    			try {
    				is.close();
    				baos.close();
    			} catch (IOException e) {
    				e.printStackTrace();
    			}
    			System.gc();
    		}
    		return baos.toByteArray();
    	}
    
    	private static double getScaling(int src, int des) {
    		/**
    		 * 48 目标尺寸÷原尺寸 sqrt开方,得出宽高百分比 49
    		 */
    		double scale = Math.sqrt((double) des / (double) src);
    		return scale;
    	}
    
    	public static int computeSampleSize(BitmapFactory.Options options,
    			int minSideLength, int maxNumOfPixels) {
    		int initialSize = computeInitialSampleSize(options, minSideLength,
    				maxNumOfPixels);
    
    		int roundedSize;
    		if (initialSize <= 8) {
    			roundedSize = 1;
    			while (roundedSize < initialSize) {
    				roundedSize <<= 1;
    			}
    		} else {
    			roundedSize = (initialSize + 7) / 8 * 8;
    		}
    
    		return roundedSize;
    	}
    
    	private static int computeInitialSampleSize(BitmapFactory.Options options,
    			int minSideLength, int maxNumOfPixels) {
    		double w = options.outWidth;
    		double h = options.outHeight;
    
    		int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math
    				.sqrt(w * h / maxNumOfPixels));
    		int upperBound = (minSideLength == -1) ? 128 : (int) Math.min(
    				Math.floor(w / minSideLength), Math.floor(h / minSideLength));
    
    		if (upperBound < lowerBound) {
    			return lowerBound;
    		}
    
    		if ((maxNumOfPixels == -1) && (minSideLength == -1)) {
    			return 1;
    		} else if (minSideLength == -1) {
    			return lowerBound;
    		} else {
    			return upperBound;
    		}
    	}



    然后在我需要加载图片BitMap的地方来调用Util.decodeBitmap():

    1. Bitmap bitmap = BitmapFactory.decodeByteArray(Util.decodeBitmap(imagePath), 0, Util.decodeBitmap(imagePath).length);  
    2.                 imageCache.put(imagePath, new SoftReference<Bitmap>(bitmap));  
    Bitmap bitmap = BitmapFactory.decodeByteArray(Util.decodeBitmap(imagePath), 0, Util.decodeBitmap(imagePath).length);
                    imageCache.put(imagePath, new SoftReference<Bitmap>(bitmap));

     


    上面这两行是我的AsyncImageLoaderByPath类中的代码,用来加载SD卡里面的图片,该类完整代码是:

    1. package com.pioneer.travel.util;  
    2.   
    3. import java.io.IOException;  
    4. import java.io.InputStream;  
    5. import java.lang.ref.SoftReference;  
    6. import java.net.MalformedURLException;  
    7. import java.net.URL;  
    8. import java.util.HashMap;  
    9.   
    10. import android.content.Context;  
    11. import android.graphics.Bitmap;  
    12. import android.graphics.BitmapFactory;  
    13. import android.graphics.BitmapFactory.Options;  
    14. import android.graphics.drawable.Drawable;  
    15. import android.os.Handler;  
    16. import android.os.Message;  
    17. import android.provider.MediaStore;  
    18. import android.util.Log;  
    19. import android.widget.ImageView;  
    20.   
    21. public class AsyncImageLoaderByPath {  
    22.     //SoftReference是软引用,是为了更好的为了系统回收变量   
    23.     private HashMap<String, SoftReference<Bitmap>> imageCache;  
    24.     private Context context;  
    25.       
    26.     public AsyncImageLoaderByPath(Context context) {  
    27.         this.imageCache = new HashMap<String, SoftReference<Bitmap>>();  
    28.         this.context = context;  
    29.     }  
    30.     public Bitmap loadBitmapByPath(final String imagePath, final ImageView imageView, final ImageCallback imageCallback){  
    31.         if (imageCache.containsKey(imagePath)) {  
    32.             //从缓存中获取   
    33.             SoftReference<Bitmap> softReference = imageCache.get(imagePath);  
    34.             Bitmap bitmap = softReference.get();  
    35.             if (bitmap != null) {  
    36.                 return bitmap;  
    37.             }  
    38.         }  
    39.         final Handler handler = new Handler() {  
    40.             public void handleMessage(Message message) {  
    41.                 imageCallback.imageLoaded((Bitmap) message.obj, imageView, imagePath);  
    42.             }  
    43.         };  
    44.         //建立新一个获取SD卡的图片   
    45.         new Thread() {  
    46.             @Override  
    47.             public void run() {  
    48.                 Bitmap bitmap = BitmapFactory.decodeByteArray(Util.decodeBitmap(imagePath), 0, Util.decodeBitmap(imagePath).length);  
    49.                 imageCache.put(imagePath, new SoftReference<Bitmap>(bitmap));  
    50.                 Message message = handler.obtainMessage(0, bitmap);  
    51.                 handler.sendMessage(message);  
    52.             }  
    53.         }.start();  
    54.         return null;  
    55.     }  
    56.     //回调接口   
    57.     public interface ImageCallback {  
    58.         public void imageLoaded(Bitmap imageBitmap,ImageView imageView, String imagePath);  
    59.     }  
    60. }  
    package com.pioneer.travel.util;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.lang.ref.SoftReference;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.util.HashMap;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.BitmapFactory.Options;
    import android.graphics.drawable.Drawable;
    import android.os.Handler;
    import android.os.Message;
    import android.provider.MediaStore;
    import android.util.Log;
    import android.widget.ImageView;
    
    public class AsyncImageLoaderByPath {
        //SoftReference是软引用,是为了更好的为了系统回收变量
        private HashMap<String, SoftReference<Bitmap>> imageCache;
        private Context context;
        
        public AsyncImageLoaderByPath(Context context) {
            this.imageCache = new HashMap<String, SoftReference<Bitmap>>();
            this.context = context;
        }
        public Bitmap loadBitmapByPath(final String imagePath, final ImageView imageView, final ImageCallback imageCallback){
            if (imageCache.containsKey(imagePath)) {
                //从缓存中获取
                SoftReference<Bitmap> softReference = imageCache.get(imagePath);
                Bitmap bitmap = softReference.get();
                if (bitmap != null) {
                    return bitmap;
                }
            }
            final Handler handler = new Handler() {
                public void handleMessage(Message message) {
                    imageCallback.imageLoaded((Bitmap) message.obj, imageView, imagePath);
                }
            };
            //建立新一个获取SD卡的图片
            new Thread() {
                @Override
                public void run() {
                	Bitmap bitmap = BitmapFactory.decodeByteArray(Util.decodeBitmap(imagePath), 0, Util.decodeBitmap(imagePath).length);
                    imageCache.put(imagePath, new SoftReference<Bitmap>(bitmap));
                    Message message = handler.obtainMessage(0, bitmap);
                    handler.sendMessage(message);
                }
            }.start();
            return null;
        }
        //回调接口
        public interface ImageCallback {
            public void imageLoaded(Bitmap imageBitmap,ImageView imageView, String imagePath);
        }
    }



    通过这个实例,我验证了一下,一次性获取20张5MB的照片,都可以加载的很流畅,完全没有再出现报OOM的错误了

    以下是运行效果

    SD卡中的图片:


     

    进入应用,选择11张照片进行滑动预览:



     


     

    希望以上所写对大家有帮助,谢谢!

  • 相关阅读:
    【More Effective C++】Item 4
    【More Effective C++】Item 3
    【More Effective C++】Item 2
    【More Effective C++】Item 1
    ”win7笔记本共享无线网络,手机连接成功却无法上网“的解决之道【亲身经历】
    【RFID防碰撞协议/算法】动态二进制搜索算法
    【RFID防碰撞协议/算法】二进制搜索防碰撞算法
    总结ASP标准控件
    总结ASP控件属性
    ..........
  • 原文地址:https://www.cnblogs.com/wangluochong/p/3949215.html
Copyright © 2011-2022 走看看