You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 11:12:12