Android:Bitmap压缩引发内存溢出问题求助
Android 图片压缩上传服务器完整实现方案
我日常处理Android端图片上传需求时,一直用这套压缩流程,能很好平衡图片质量与文件大小,适配相册选取、相机拍摄的场景,给你拆解下具体实现:
核心压缩步骤
- 获取图片路径:不管是相册选择的结果还是相机拍摄后的文件,先拿到对应的本地路径(注意Android 10+的Scoped Storage适配,这里默认你已经处理好权限和路径兼容逻辑)
- 高效加载Bitmap:先读取图片宽高信息,再按需缩放加载,避免直接加载大尺寸原图导致OOM
- 按最大宽高缩放:设置合理的最大宽高阈值(比如1080*1920),计算缩放比例,把图片尺寸控制在阈值内
- 修正图片旋转角度:相机拍摄的图片常存在旋转偏移问题,必须读取EXIF信息纠正角度,避免上传后图片显示异常
- 质量压缩输出:对缩放后的Bitmap进行质量压缩,生成最终的压缩文件或Bitmap对象
完整实现代码
import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; import android.media.ExifInterface; import android.os.Build; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; public class ImageCompressor { // 可根据业务需求调整的最大宽高阈值 private static final float MAX_WIDTH = 1080f; private static final float MAX_HEIGHT = 1920f; // 质量压缩默认值(0-100,数值越高质量越好、文件越大) private static final int DEFAULT_QUALITY = 80; /** * 压缩图片的核心方法 * @param imagePath 原始图片路径 * @param outputPath 压缩后图片的输出路径 * @return 压缩后的Bitmap,失败返回null */ public static Bitmap compressImage(String imagePath, String outputPath) { Bitmap compressedBitmap = null; try { // 步骤1:仅读取图片宽高,不加载完整Bitmap BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(imagePath, options); // 步骤2:计算缩放比例 int originalWidth = options.outWidth; int originalHeight = options.outHeight; float scaleFactor = calculateScaleFactor(originalWidth, originalHeight); // 步骤3:加载缩放后的Bitmap,优化内存占用 options.inJustDecodeBounds = false; options.inSampleSize = (int) scaleFactor; options.inPreferredConfig = Bitmap.Config.RGB_565; // 比ARGB_8888节省一半内存 Bitmap tempBitmap = BitmapFactory.decodeFile(imagePath, options); // 步骤4:读取EXIF信息,修正图片旋转角度 int rotationAngle = getImageRotationAngle(imagePath); if (rotationAngle != 0) { tempBitmap = rotateBitmap(tempBitmap, rotationAngle); } // 步骤5:质量压缩并保存到指定路径 if (tempBitmap != null) { FileOutputStream fos = new FileOutputStream(outputPath); tempBitmap.compress(Bitmap.CompressFormat.JPEG, DEFAULT_QUALITY, fos); fos.flush(); fos.close(); compressedBitmap = BitmapFactory.decodeFile(outputPath); tempBitmap.recycle(); // 释放临时Bitmap内存 } } catch (IOException e) { e.printStackTrace(); } return compressedBitmap; } /** * 计算缩放比例,确保图片不超过最大宽高 */ private static float calculateScaleFactor(int width, int height) { float scale = 1f; if (width > MAX_WIDTH || height > MAX_HEIGHT) { float widthScale = MAX_WIDTH / width; float heightScale = MAX_HEIGHT / height; scale = Math.min(widthScale, heightScale); } return scale; } /** * 从EXIF信息中获取图片旋转角度 */ private static int getImageRotationAngle(String imagePath) throws IOException { int rotation = 0; ExifInterface exif; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { exif = new ExifInterface(new File(imagePath)); } else { exif = new ExifInterface(imagePath); } int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); switch (orientation) { case ExifInterface.ORIENTATION_ROTATE_90: rotation = 90; break; case ExifInterface.ORIENTATION_ROTATE_180: rotation = 180; break; case ExifInterface.ORIENTATION_ROTATE_270: rotation = 270; break; default: rotation = 0; } return rotation; } /** * 将Bitmap旋转到指定角度 */ private static Bitmap rotateBitmap(Bitmap bitmap, int angle) { Matrix matrix = new Matrix(); matrix.postRotate(angle); return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); } }
实用提示
- 内存优化:使用
Bitmap.Config.RGB_565配置加载Bitmap,能有效降低内存占用 - 权限适配:Android 10及以上建议通过MediaStore获取和保存图片,避免直接操作文件路径的权限问题;低版本需申请
READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE权限 - 质量调整:
DEFAULT_QUALITY可根据业务需求灵活调整,比如追求高清可设为90,追求小文件可设为70
内容的提问来源于stack exchange,提问作者user9609225
相关产品推荐
相关产品推荐

