Android: 縮放圖片文件引起的OOM異常
https://dev.10086.cn/cmdn/wiki/index.php?doc-view-5037.html
傳輸文件,或者設置頭像,我們一般都會檢查原始圖片的大小,作縮放處理。
常用的Java版縮放圖片代碼:
public Bitmap getZoomImage(Bitmap src, int desW, int desH) { Bitmap desImg = null; int srcW = src.getWidth(); // 原始圖像寬 int srcH = src.getHeight(); // 原始圖像高 int[] srcBuf = new int[srcW * srcH]; // 原始圖片像素信息緩存 src.getPixels(srcBuf, 0, srcW, 0, 0, srcW, srcH); // 計算插值表 int[] tabY = new int[desH]; int[] tabX = new int[desW]; int sb = 0; int db = 0; int tems = 0; int temd = 0; int distance = srcH > desH ? srcH : desH; for (int i = 0; i <= distance; i++) {/* 垂直方向 */ tabY[db] = sb; tems += srcH; temd += desH; if (tems > distance) { tems -= distance; sb++; } if (temd > distance) { temd -= distance; db++; } } sb = 0; db = 0; tems = 0; temd = 0; distance = srcW > desW ? srcW : desW; for (int i = 0; i <= distance; i++) {/* 水平方向 */ tabX[db] = (short) sb; tems += srcW; temd += desW; if (tems > distance) { tems -= distance; sb++; } if (temd > distance) { temd -= distance; db++; } } // 生成放大縮小後圖形像素 int[] desBuf = new int[desW * desH]; int dx = 0; int dy = 0; int sy = 0; int oldy = -1; for (int i = 0; i < desH; i++) { if (oldy == tabY[i]) { System.arraycopy(desBuf, dy - desW, desBuf, dy, desW); } else { dx = 0; for (int j = 0; j < desW; j++) { desBuf[dy + dx] = srcBuf[sy + tabX[j]]; dx++; } sy += (tabY[i] - oldy) * srcW; } oldy = tabY[i]; dy += desW; } // 生成圖片 desImg = Bitmap.createBitmap(desBuf, desW, desH, Bitmap.Config.ARGB_8888); return desImg; }
常用的Android版縮放圖片代碼:
ContentResolver cr = this.getContentResolver(); try { InputStream in = cr.openInputStream(uri); Bitmap bitmap = BitmapFactory.decodeStream(in); try { in.close(); } catch (IOException e) { e.printStackTrace(); } if(null == bitmap) { Toast.makeText(this, "Head is not set successful,Decode bitmap failure", 2000); } //原始圖片的尺寸 int bmpWidth = bitmap.getWidth(); int bmpHeight = bitmap.getHeight(); //縮放圖片的尺寸 float scaleWidth = (float) 40 / bmpWidth; float scaleHeight = (float) 40 / bmpHeight; Matrix matrix = new Matrix(); matrix.postScale(scaleWidth, scaleHeight); //產生縮放後的Bitmap對象 Bitmap resizeBitmap = Bitmap.createBitmap( bitmap, 0, 0, bmpWidth, bmpHeight, matrix, false); bitmap.recycle(); //Bitmap to byte[] byte[] photoData = Bitmap2Bytes(resizeBitmap); //save file String fileName = "/sdcard/test.jpg"; FileUtil.writeToFile(fileName, photoData); //save photo check sum to db DataCenter.GetInstance().ModifyIMMUser(); //refresh ImageView } catch (FileNotFoundException exp) { exp.printStackTrace(); }
如果圖片非常大,在執行BitmapFactory.decodeStream的時候就會拋出OOM異常。
我們來看看係統應用MMS是如何處理的,SMS添加了多媒體附件後就作MMS處理了,當附加文件原圖超過300K,也會做個縮放處理,具體參考:com.android.mms.ui/.UriImage:
package com.android.mms.ui; public class UriImage { private int mWidth; private int mHeight; ... ... // private void decodeBoundsInfo() { InputStream input = null; try { input = mContext.getContentResolver().openInputStream(mUri); BitmapFactory.Options opt = new BitmapFactory.Options(); opt.inJustDecodeBounds = true;//隻描邊,不讀取數據 BitmapFactory.decodeStream(input, null, opt); mWidth = opt.outWidth; mHeight = opt.outHeight; } catch (FileNotFoundException e) { // Ignore Log.e(TAG, "IOException caught while opening stream", e); } finally { if (null != input) { try { input.close(); } catch (IOException e) { // Ignore Log.e(TAG, "IOException caught while closing stream", e); } } } } private byte[] getResizedImageData(int widthLimit, int heightLimit) { int outWidth = mWidth; int outHeight = mHeight; int s = 1; while ((outWidth / s > widthLimit) || (outHeight / s > heightLimit)) { s *= 2; } //先設置選項 BitmapFactory.Options options = new BitmapFactory.Options(); //returning a smaller image to save memory. options.inSampleSize = s; InputStream input = null; try { input = mContext.getContentResolver().openInputStream(mUri); Bitmap b = BitmapFactory.decodeStream(input, null, options);//注意看options的用法 if (b == null) { return null; } ByteArrayOutputStream os = new ByteArrayOutputStream(); b.compress(CompressFormat.JPEG, MessageUtils.IMAGE_COMPRESSION_QUALITY, os); return os.toByteArray(); } catch (FileNotFoundException e) { Log.e(TAG, e.getMessage(), e); return null; } finally { if (input != null) { try { input.close(); } catch (IOException e) { Log.e(TAG, e.getMessage(), e); } } } } ... ... }
可以看出,MMS應用的方法是:先設置縮放選項,再讀取縮放的圖片數據到內存,規避了內存引起的OOM。
修改後的代碼:
ContentResolver cr = this.getContentResolver(); try { InputStream in = cr.openInputStream(uri); BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeStream(in, null, options); try { in.close(); } catch (IOException e) { e.printStackTrace(); } int mWidth = options.outWidth; int mHeight = options.outHeight; int sWidth = 40; int sHeight = 40; int s = 1; while ((mWidth / s > sWidth * 2) || (mHeight / s > sHeight * 2)) { s *= 2; } options = new BitmapFactory.Options(); options.inSampleSize = s; in = cr.openInputStream(uri); Bitmap bitmap = BitmapFactory.decodeStream(in, null, options); try { in.close(); } catch (IOException e) { e.printStackTrace(); } if(null == bitmap) { Toast.makeText(this, "Head is not set successful,Decode bitmap failure", 2000); return ; } //原始圖片的尺寸 int bmpWidth = bitmap.getWidth(); int bmpHeight = bitmap.getHeight(); //縮放圖片的尺寸 float scaleWidth = (float) sWidth / bmpWidth; float scaleHeight = (float) sHeight / bmpHeight; Matrix matrix = new Matrix(); matrix.postScale(scaleWidth, scaleHeight); //產生縮放後的Bitmap對象 Bitmap resizeBitmap = Bitmap.createBitmap( bitmap, 0, 0, bmpWidth, bmpHeight, matrix, false); bitmap.recycle(); // Bitmap resizeBitmap = bitmap; //Bitmap to byte[] byte[] photoData = bitmap2Bytes(resizeBitmap); //save file String fileName = "/sdcard/test.jpg"; FileUtil.writeToFile(fileName, photoData);
private byte[] bitmap2Bytes(Bitmap bm) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); bm.compress(Bitmap.CompressFormat.JPEG, 100, baos); return baos.toByteArray(); }
最後更新:2017-04-02 17:09:25