很多時候,如果APP需要下載和加載很多圖片(尤其是大圖片)的時候,就往往會報如下圖所示的錯誤:
如上圖所示,OOM(OutOfMemoryError)表示內存溢出,這是因爲網絡或內存中的圖片被加載成Bitmap時耗費的內存超出了系統內存而造成內存溢出。解決這個問題有很多方法,這裏主要介紹其中的一種方法:圖片壓縮。
這裏貼出一個工具類:
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
/**
* 圖片壓縮工具類
*/
public class ImageUtil {
/**
* 從指定路徑獲取Bitmap圖片
*
* @param imgPath 原始圖片的路徑
*/
public static Bitmap getBitmap(String imgPath) {
// Get bitmap through image path
BitmapFactory.Options newOpts = new BitmapFactory.Options();
newOpts.inJustDecodeBounds = false;
newOpts.inPurgeable = true;
newOpts.inInputShareable = true;
// Do not compress
newOpts.inSampleSize = 1;
newOpts.inPreferredConfig = Config.RGB_565;
return BitmapFactory.decodeFile(imgPath, newOpts);
}
/**
* 將圖片存儲到指定路徑的文件中
*
* @param bitmap 原始圖片Bitmap
* @param outPath 目標圖片路徑
*/
public static void storeImage(Bitmap bitmap, String outPath) throws FileNotFoundException {
FileOutputStream os = new FileOutputStream(outPath);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
}
/**
* 尺寸壓縮(改變圖片的寬高),適合用於加載縮略圖
*
* @param imgPath 圖片路徑
* @param pixelW 想要將圖片壓縮到的寬度
* @param pixelH 想要將圖片壓縮到的高度
*/
public Bitmap ratio(String imgPath, float pixelW, float pixelH) {
BitmapFactory.Options newOpts = new BitmapFactory.Options();
// 開始讀入圖片,此時把options.inJustDecodeBounds 設回true,即只讀邊不讀內容
newOpts.inJustDecodeBounds = true;
newOpts.inPreferredConfig = Config.RGB_565;
// Get bitmap info, but notice that bitmap is null now
Bitmap bitmap = BitmapFactory.decodeFile(imgPath, newOpts);
newOpts.inJustDecodeBounds = false;
int w = newOpts.outWidth;
int h = newOpts.outHeight;
// 想要縮放的目標尺寸
float hh = pixelH;// 設置高度爲240f時,可以明顯看到圖片縮小了
float ww = pixelW;// 設置寬度爲120f,可以明顯看到圖片縮小了
// 縮放比。由於是固定比例縮放,只用高或者寬其中一個數據進行計算即可
int be = 1;//be=1表示不縮放
if (w > h && w > ww) {//如果寬度大的話根據寬度固定大小縮放
be = (int) (newOpts.outWidth / ww);
} else if (w < h && h > hh) {//如果高度高的話根據寬度固定大小縮放
be = (int) (newOpts.outHeight / hh);
}
if (be <= 0) be = 1;
newOpts.inSampleSize = be;//設置縮放比例
// 開始壓縮圖片,注意此時已經把options.inJustDecodeBounds 設回false了
bitmap = BitmapFactory.decodeFile(imgPath, newOpts);
return bitmap;
}
/**
* 尺寸壓縮(改變圖片的寬高),適合用於加載縮略圖
*
* @param image 圖片的Bitmap
* @param pixelW 想要將圖片壓縮到的寬度
* @param pixelH 想要將圖片壓縮到的高度
*/
public Bitmap ratio(Bitmap image, float pixelW, float pixelH) {
ByteArrayOutputStream os = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, os);
if (os.toByteArray().length / 1024 > 1024) {//判斷如果圖片大於1M,進行壓縮避免在生成圖片(BitmapFactory.decodeStream)時溢出
os.reset();//重置baos即清空baos
image.compress(Bitmap.CompressFormat.JPEG, 50, os);//這裏壓縮50%,把壓縮後的數據存放到baos中
}
ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
BitmapFactory.Options newOpts = new BitmapFactory.Options();
//開始讀入圖片,此時把options.inJustDecodeBounds 設回true了
newOpts.inJustDecodeBounds = true;
newOpts.inPreferredConfig = Config.RGB_565;
Bitmap bitmap = BitmapFactory.decodeStream(is, null, newOpts);
newOpts.inJustDecodeBounds = false;
int w = newOpts.outWidth;
int h = newOpts.outHeight;
float hh = pixelH;// 設置高度爲240f時,可以明顯看到圖片縮小了
float ww = pixelW;// 設置寬度爲120f,可以明顯看到圖片縮小了
//縮放比。由於是固定比例縮放,只用高或者寬其中一個數據進行計算即可
int be = 1;//be=1表示不縮放
if (w > h && w > ww) {//如果寬度大的話根據寬度固定大小縮放
be = (int) (newOpts.outWidth / ww);
} else if (w < h && h > hh) {//如果高度高的話根據寬度固定大小縮放
be = (int) (newOpts.outHeight / hh);
}
if (be <= 0) be = 1;
newOpts.inSampleSize = be;//設置縮放比例
//重新讀入圖片,注意此時已經把options.inJustDecodeBounds 設回false了
is = new ByteArrayInputStream(os.toByteArray());
bitmap = BitmapFactory.decodeStream(is, null, newOpts);
return bitmap;
}
/**
* 質量壓縮,然後存儲到指定路徑
*
* @param image 原始圖片Bitmap
* @param outPath 目標圖片路徑
* @param maxSize 圖片將被壓縮成這麼多KB
*/
public void compressAndGenImage(Bitmap image, String outPath, int maxSize) throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
// scale
int options = 100;
// Store the bitmap into output stream(no compress)
image.compress(Bitmap.CompressFormat.JPEG, options, os);
// Compress by loop
while (os.toByteArray().length / 1024 > maxSize) {
// Clean up os
os.reset();
// interval 10
options -= 10;
image.compress(Bitmap.CompressFormat.JPEG, options, os);
}
// Generate compressed image file
FileOutputStream fos = new FileOutputStream(outPath);
fos.write(os.toByteArray());
fos.flush();
fos.close();
}
/**
* 質量壓縮並存儲到指定路徑
*
* @param imgPath 想要壓縮的圖片路徑
* @param outPath 目標圖片路徑
* @param maxSize 圖片將被壓縮成這麼多KB
* @param needsDelete 壓縮後是否刪除原始圖片
*/
public void compressAndGenImage(String imgPath, String outPath, int maxSize, boolean needsDelete) throws IOException {
compressAndGenImage(getBitmap(imgPath), outPath, maxSize);
if (needsDelete) {
File file = new File(imgPath);
if (file.exists()) {
file.delete();
}
}
}
/**
* 尺寸壓縮後保存圖片
*
* @param image 原始圖片Bitmap
* @param outPath 目標圖片路徑
* @param pixelW 想要將圖片壓縮到的寬度
* @param pixelH 想要將圖片壓縮到的高度
*/
public void ratioAndGenThumb(Bitmap image, String outPath, float pixelW, float pixelH) throws FileNotFoundException {
Bitmap bitmap = ratio(image, pixelW, pixelH);
storeImage(bitmap, outPath);
}
/**
* 尺寸壓縮後保存圖片,並決定是否刪除原始圖片
*
* @param imgPath 想要壓縮的圖片路徑
* @param outPath 目標圖片路徑
* @param pixelW 想要將圖片壓縮到的寬度
* @param pixelH 想要將圖片壓縮到的高度
* @param needsDelete 是否刪除原始圖片
*/
public void ratioAndGenThumb(String imgPath, String outPath, float pixelW, float pixelH, boolean needsDelete) throws FileNotFoundException {
Bitmap bitmap = ratio(imgPath, pixelW, pixelH);
storeImage(bitmap, outPath);
if (needsDelete) {
File file = new File(imgPath);
if (file.exists()) {
file.delete();
}
}
}
}