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

Marshmallow系统调用setImageResource()触发OutOfMemoryError求助

解决Marshmallow系统中setImageResource()触发OutOfMemoryError的问题

嘿,我来帮你搞定这个恼人的OOM问题!虽然你的图片尺寸看起来不大(797x800),但Android加载图片时的默认行为很容易在旧系统(比如Marshmallow)上触发内存溢出,尤其是你重复点击按钮生成内容的时候。下面是几个针对性的解决方案,按优先级排序:

1. 用图片加载库一键解决(最省心)

直接用Glide这类成熟的图片加载库,它们会自动帮你处理图片缩放、内存缓存和回收,完美适配旧系统。代码超简单:

Glide.with(this)
     .load(R.drawable.your_downloaded_image)
     .into(yourPhotoView); // 不管是普通ImageView还是PhotoView都能用

这类库会根据你的ImageView实际尺寸自动解码出最合适的Bitmap,还能避免重复加载导致的内存堆积,几乎是零成本解决问题。

2. 手动优化Bitmap解码(自定义程度高)

如果你不想引入第三方库,那就手动控制Bitmap的解码过程,减少内存占用:

第一步:编写图片解码工具方法

这个方法会先获取图片原始尺寸,计算合适的缩放比例,再用更节省内存的配置解码:

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
                                                     int targetWidth, int targetHeight) {
    // 先获取图片原始尺寸,不加载进内存
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // 计算缩放比例(inSampleSize是2的幂,保证解码效率)
    options.inSampleSize = calculateInSampleSize(options, targetWidth, targetHeight);

    // 正式解码图片,用RGB_565配置(每个像素占2字节,比默认ARGB_8888省一半内存)
    options.inJustDecodeBounds = false;
    options.inPreferredConfig = Bitmap.Config.RGB_565;
    return BitmapFactory.decodeResource(res, resId, options);
}

private static int calculateInSampleSize(BitmapFactory.Options options, int targetWidth, int targetHeight) {
    final int originalWidth = options.outWidth;
    final int originalHeight = options.outHeight;
    int inSampleSize = 1;

    if (originalHeight > targetHeight || originalWidth > targetWidth) {
        final int halfHeight = originalHeight / 2;
        final int halfWidth = originalWidth / 2;

        // 找到最大的缩放比例,让解码后图片尺寸不小于目标尺寸
        while ((halfHeight / inSampleSize) >= targetHeight
                && (halfWidth / inSampleSize) >= targetWidth) {
            inSampleSize *= 2;
        }
    }
    return inSampleSize;
}

第二步:在ImageView布局完成后加载图片

一定要等ImageView的实际宽高确定后再解码,不然缩放比例会算错。可以用ViewTreeObserver来监听:

yourImageView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        // 移除监听,避免重复执行
        yourImageView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
        
        int targetWidth = yourImageView.getWidth();
        int targetHeight = yourImageView.getHeight();
        
        // 加载优化后的Bitmap
        Bitmap optimizedBitmap = decodeSampledBitmapFromResource(getResources(), R.drawable.your_image, targetWidth, targetHeight);
        yourImageView.setImageBitmap(optimizedBitmap);
    }
});

第三步:重复加载时回收旧Bitmap

点击按钮切换图片时,记得回收之前的Bitmap,避免内存堆积:

// 设置新图片前先回收旧的
Drawable oldDrawable = yourImageView.getDrawable();
if (oldDrawable instanceof BitmapDrawable) {
    Bitmap oldBitmap = ((BitmapDrawable) oldDrawable).getBitmap();
    if (oldBitmap != null && !oldBitmap.isRecycled()) {
        oldBitmap.recycle();
    }
}
// 再加载新的Bitmap
Bitmap newBitmap = decodeSampledBitmapFromResource(getResources(), R.drawable.new_image, targetWidth, targetHeight);
yourImageView.setImageBitmap(newBitmap);

3. 避免系统自动缩放图片

如果你的图片放在普通drawable目录,Android会根据屏幕密度自动缩放它(比如xxhdpi屏幕会把图片放大1.5倍),导致解码后的Bitmap尺寸变大,内存占用飙升。解决办法是把图片放到drawable-nodpi目录,让系统保持图片原始尺寸,不做自动缩放。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 10:48:44