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
相关产品推荐
相关产品推荐

