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

调用MediaStore.getBitmap获取相机图片时触发内存不足错误求助

解决相机图片加载时的OutOfMemoryError问题

我太懂这个头疼的问题了!现在手机相机拍的照片分辨率动辄几千万像素,直接用MediaStore.Images.Media.getBitmap()加载整张图,内存根本扛不住——这个方法会把图片的完整像素数据一股脑塞进内存,触发OOM简直是必然的。

问题根源

MediaStore.Images.Media.getBitmap() 底层其实就是调用BitmapFactory.decodeStream(),但完全没做内存优化,直接加载原始尺寸的图片。比如一张4000×3000的ARGB_8888格式图片,内存占用就有4000×3000×4=48MB,要是App的内存配额不够,直接就炸了。

解决方案:用BitmapFactory.Options做采样缩放

我们可以通过BitmapFactory.Options加载缩小版的图片,只保留我们需要的尺寸,大幅降低内存占用。具体步骤如下:

  1. 先获取图片原始宽高,但不加载到内存(避免提前占内存)
  2. 根据ImageView的尺寸计算合适的采样率(采样率为2的整数倍,比如设为2,图片宽高就会变成原来的1/2,内存占用直接减为1/4)
  3. 用计算好的采样率加载缩小后的图片

另外还要修正你代码里的一个小bug:50 / 100是整数除法,结果是0,压缩质量应该直接传50(代表50%的压缩质量)。

修改后的完整代码

public void grabImage(ImageView imageView) {
    this.getContentResolver().notifyChange(mImageUri, null);
    ContentResolver cr = this.getContentResolver();
    Bitmap bitmap = null;
    try {
        // 1. 仅获取图片原始尺寸,不加载到内存
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        InputStream inputStream = cr.openInputStream(mImageUri);
        BitmapFactory.decodeStream(inputStream, null, options);
        inputStream.close();

        // 2. 根据ImageView尺寸计算采样率
        int reqWidth = imageView.getWidth();
        int reqHeight = imageView.getHeight();
        int inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

        // 3. 用采样率加载缩小后的图片
        options.inJustDecodeBounds = false;
        options.inSampleSize = inSampleSize;
        options.inPreferredConfig = Bitmap.Config.RGB_565; // 用RGB_565替代ARGB_8888,内存减半
        inputStream = cr.openInputStream(mImageUri);
        bitmap = BitmapFactory.decodeStream(inputStream, null, options);
        inputStream.close();

        // 修复压缩参数错误:直接传50而非50/100
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.JPEG, 50, byteArrayOutputStream);
        getImageUri(this, bitmap);
        imageView.setImageBitmap(bitmap);
    } catch (IOException e) {
        Toast.makeText(this, "Failed to load", Toast.LENGTH_SHORT).show();
        Log.d(TAG, "Failed to load", e);
    } catch (OutOfMemoryError e) {
        e.printStackTrace();
        Toast.makeText(this, "内存不足,无法加载图片", Toast.LENGTH_SHORT).show();
    } finally {
        // 可选:当图片不再使用时,及时回收释放内存
        // if (bitmap != null && !bitmap.isRecycled()) {
        //     bitmap.recycle();
        // }
    }
}

// 计算合适的采样率工具方法
private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {
        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // 找到最大的采样率,确保缩放后的图片尺寸不小于需求尺寸
        while ((halfHeight / inSampleSize) >= reqHeight
                && (halfWidth / inSampleSize) >= reqWidth) {
            inSampleSize *= 2;
        }
    }
    return inSampleSize;
}

额外优化小技巧

  • 优先用Bitmap.Config.RGB_565:除非你需要透明通道,否则这个格式每个像素只占2字节,比默认的ARGB_8888省一半内存。
  • 及时回收Bitmap:当图片不再显示时,调用bitmap.recycle()释放内存,注意别在还在使用Bitmap的时候调用。
  • 考虑用Glide/Picasso等图片库:这些库已经封装了完善的内存优化、缓存机制,能更省心地处理图片加载,从根源上避免OOM。

内容的提问来源于stack exchange,提问作者Developer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 09:51:36