Androidが複数の大図をロードしたことによるOutOfMemoryErrorの解決


転載先:http://blog.csdn.net/wulianghuan/article/details/11548373
最近、プロジェクトでは複数の写真を選択したり撮影したりした後、スライドプレビューとアップロードを提供する必要があるため、多くの写真は何MBも1枚である.現在のAndroidシステムは実行するプログラムに一定のメモリ制限があるため、一般的には16 MBか24 MB(プラットフォームによって異なる)であり、処理せずに直接ロードすると必ずOOM(Out Of Memmory)に報告される.ネット上にはandroidロードbitmapメモリオーバーフローを解決する方法がたくさんあります.一般的な方法をまとめました.以下は私が開発事例から抽出したコードです.
プロジェクトにUtil.javaツールクラスを作成しました.画像のパスに基づいてバイトストリーム配列オブジェクトを返します.
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()を呼び出します.
Bitmap bitmap = BitmapFactory.decodeByteArray(Util.decodeBitmap(imagePath), 0, Util.decodeBitmap(imagePath).length);
                imageCache.put(imagePath, new SoftReference<Bitmap>(bitmap));

上の2行は私のAsyncImageLoaderByPathクラスのコードで、SDカードの中の画像をロードするために使用されています.このクラスの完全なコードは:
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枚の5 MBの写真を取得しても、ロードがスムーズで、OOMのエラーが全く発生していないことを検証しました.