android 縮放圖片引起的內存溢出

編寫:徐建祥([email protected])
日期:2010/12/06
網址:http://www.anymobile.org

傳輸文件,或者設置頭像,我們一般都會檢查原始圖片的大小,作縮放處理。

常用的Java版縮放圖片代碼:

view plaincopy to clipboardprint?

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版縮放圖片代碼:

view plaincopy to clipboardprint?

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:

view plaincopy to clipboardprint?

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。

修改後的代碼:

view plaincopy to clipboardprint?

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);



view plaincopy to clipboardprint?

private byte[] bitmap2Bytes(Bitmap bm)
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.JPEG, 100, baos);
return baos.toByteArray();
}



OVER!
發佈了32 篇原創文章 · 獲贊 1 · 訪問量 1270
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章