Bitmap bmp = BitmapFactory.decodeFile(pePicFile.getAbsolutePath() + "/"+info.getImage());
上面參數是我將要讀取的圖片文件及路徑,當文件較小時,程序能夠正常運行,但是當我選擇一張大圖時,程序立刻蹦出了java.lang.OutOfMemoryError: bitmap size exceeds VM budget的OOM錯誤!
在android設備上(where you have only 16MB memory available),如果使用BitmapFactory解碼一個較大文件,很大的情況下會出現上述情況。那么,怎么解決?!
先說之前提到過的一種方法:即將載入的圖片縮小,這種方式以犧牲圖片的質量為代價。在BitmapFactory中有一個內部類BitmapFactory.Options,其中當options.inSampleSize值>1時,根據文檔:
If set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save memory. (1 -> decodes full size; 2 -> decodes 1/4th size; 4 -> decode 1/16th size). Because you rarely need to show and have full size bitmap images on your phone. For manipulations smaller sizes are usually enough.
也就是說,options.inSampleSize是以2的指數的倒數被進行放縮。這樣,我們可以依靠inSampleSize的值的設定將圖片放縮載入,這樣一般情況也就不會出現上述的OOM問題了。現在問題是怎么確定inSampleSize的值?每張圖片的放縮大小的比例應該是不一樣的!這樣的話就要運行時動態確定。在BitmapFactory.Options中提供了另一個成員inJustDecodeBounds。
BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inJustDecodeBounds = true; Bitmap bitmap = BitmapFactory.decodeFile(imageFile, opts);
設置inJustDecodeBounds為true后,decodeFile並不分配空間,但可計算出原始圖片的長度和寬度,即opts.width和opts.height。有了這兩個參數,再通過一定的算法,即可得到一個恰當的inSampleSize。
Android提供了一種動態計算的方法。如下:
public Bitmap getBitmapFromFile(File dst, int width, int height) {
if (null != dst && dst.exists()) {
BitmapFactory.Options opts = null;
if (width > 0 && height > 0) {
opts = new BitmapFactory.Options();
//設置inJustDecodeBounds為true后,decodeFile並不分配空間,此時計算原始圖片的長度和寬度
opts.inJustDecodeBounds = true;
BitmapFactory.decodeFile(dst.getPath(), opts);
// 計算圖片縮放比例
final int minSideLength = Math.min(width, height);
opts.inSampleSize = computeSampleSize(opts, minSideLength,
width * height);
//這里一定要將其設置回false,因為之前我們將其設置成了true
opts.inJustDecodeBounds = false;
opts.inInputShareable = true;
opts.inPurgeable = true;
}
try {
return BitmapFactory.decodeFile(dst.getPath(), opts);
} catch (OutOfMemoryError e) {
e.printStackTrace();
}
}
return null;
}
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 the larger one when there is no overlapping zone.
return lowerBound;
}
if ((maxNumOfPixels == -1) && (minSideLength == -1)) {
return 1;
} else if (minSideLength == -1) {
return lowerBound;
} else {
return upperBound;
}
}
這樣,在BitmapFactory.decodeFile執行處,也就不會報出上面的OOM Error。
如前面提到的,這種方式在一定程度上是以犧牲圖片質量為代價的。如何才能更加優化的實現需求?
當在android設備中載入較大圖片資源時,可以創建一些臨時空間,將載入的資源載入到臨時空間中。
BitmapFactory.Options bfOptions=new BitmapFactory.Options(); bfOptions.inTempStorage=newbyte[12 * 1024];
以上創建了一個12kb的臨時空間。然后使用
Bitmap bitmapImage = BitmapFactory.decodeFile(path,bfOptions);
但是我在程序中卻還是出現以上問題!以下使用BitmapFactory.decodeFileDescriptor解決了以上問題:
BitmapFactory.Options bfOptions=new BitmapFactory.Options();
bfOptions.inDither=false;
bfOptions.inPurgeable=true;
bfOptions.inTempStorage=newbyte[12 * 1024];
bfOptions.inJustDecodeBounds = true;
File file = new File(pePicFile.getAbsolutePath() + "/"+info.getImage());
FileInputStream fs=null;
Bitmap bmp = null;
try {
fs = new FileInputStream(file);
if(fs != null)
bmp = BitmapFactory.decodeFileDescriptor(fs.getFD(), null, bfOptions);
} catch (Exception e) {
e.printStackTrace();
}
當然要將取得圖片進行放縮顯示等處理也可以在以上得到的bmp進行。
圖片處理后記得回收內存
if(!bmp.isRecycle() ){
bmp.recycle() //回收圖片所占的內存
system.gc() //提醒系統及時回收
}
