【Android - 进阶】之图片压缩
很多时候,如果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(); } } } }