Android 優化Bitmap避免 OutOfMemoryError
使用android提供的BitmapFactory解碼圖片時,往往會因為圖片過大而遇到OutOfMemoryError的異常。要想正常使用,一種簡便的方式是分配更少的內存空間來存儲,即在載入圖片的時候以犧牲圖片質量為代價,將圖片進行放縮,這是一種避免OOM所采用的解決方法。但是,這種方法是得不償失的,犧牲了圖片質量。
在BitmapFactory中有一個內部類BitmapFactory.Options,其中值得我們注意的是inSampleSize和inJustDecodeBounds兩個屬性:
inSampleSize是以2的指數的倒數被進行放縮
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.
inJustDecodeBounds為Boolean型
設置inJustDecodeBounds為true後,decodeFile並不分配空間,但可計算出原始圖片的長度和寬度,即options.outWidth和options.outHeight。
要對圖片進行縮放,最大的問題就是怎麼在運行時動態的改變inSampleSize的值,通過上麵的inJustDecodeBounds可以知道圖片原始的大小,那麼這樣以來就可以通過算法來得到一個恰當的inSampleSize值。其動態算法可參考下麵的,網上也很多,大體都一樣:
- /**
- * compute Sample Size
- *
- * @param options
- * @param minSideLength
- * @param maxNumOfPixels
- * @return
- */
- 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;
- }
- /**
- * compute Initial Sample Size
- *
- * @param options
- * @param minSideLength
- * @param maxNumOfPixels
- * @return
- */
- 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;
- }
- }
有了上麵的算法,我們就可以輕易的get到Bitmap了:
- /**
- * get Bitmap
- *
- * @param imgFile
- * @param minSideLength
- * @param maxNumOfPixels
- * @return
- */
- public static Bitmap tryGetBitmap(String imgFile, int minSideLength,
- int maxNumOfPixels) {
- if (imgFile == null || imgFile.length() == 0)
- return null;
- try {
- FileDescriptor fd = new FileInputStream(imgFile).getFD();
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inJustDecodeBounds = true;
- // BitmapFactory.decodeFile(imgFile, options);
- BitmapFactory.decodeFileDescriptor(fd, null, options);
- options.inSampleSize = computeSampleSize(options, minSideLength,
- maxNumOfPixels);
- try {
- // 這裏一定要將其設置回false,因為之前我們將其設置成了true
- // 設置inJustDecodeBounds為true後,decodeFile並不分配空間,即,BitmapFactory解碼出來的Bitmap為Null,但可計算出原始圖片的長度和寬度
- options.inJustDecodeBounds = false;
- Bitmap bmp = BitmapFactory.decodeFile(imgFile, options);
- return bmp == null ? null : bmp;
- } catch (OutOfMemoryError err) {
- return null;
- }
- } catch (Exception e) {
- return null;
- }
- }
最後更新:2017-04-02 17:09:28