如何在同一屏幕使用大量ImageView?解决OutOfMemoryError异常
问题本质拆解
你遇到的OOM核心原因是:系统加载非适配dpi目录下的图片时,会执行不必要的放大缩放——比如把默认drawable目录(等价于mdpi密度)的图片,加载到xxhdpi设备上时,系统会自动把图片放大3倍(xxhdpi是mdpi的3倍密度),这会让图片占用的内存瞬间飙升,超出部分设备的内存阈值,从而抛出OOM。而复制图片到各dpi目录的临时方案,虽然让系统直接加载对应密度的图片避免了缩放,但多份图片拷贝直接导致了应用体积暴涨。
更优替代方案(无需多份图片拷贝)
下面给你几个既能解决OOM,又能控制应用体积的方法,按落地优先级排序:
1. 单份图片放到最高适配dpi目录
不需要复制多份,只把图片放到目标用户群体的主流最高密度目录(比如当前多数安卓设备是xxhdpi,就放到drawable-xxhdpi)。低dpi设备会自动缩小图片,这个缩小过程的内存消耗远小于放大操作,基本不会触发OOM。如果用户覆盖多密度设备,优先选主流高密度目录存放即可,系统对缩小缩放的处理逻辑更友好。
2. 手动控制图片采样率(BitmapFactory.Options)
加载图片时通过BitmapFactory.Options设置inSampleSize,让系统只加载原图的部分像素,直接降低内存占用。示例代码:
// 根据ImageView尺寸加载适配的Bitmap public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { // 先获取图片原始尺寸,不加载实际像素 final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resId, options); // 计算合适的采样率 options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // 加载实际Bitmap options.inJustDecodeBounds = false; return BitmapFactory.decodeResource(res, resId, options); } // 计算2的幂次采样率,保证缩放后尺寸不小于需求 public static 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; }
使用时,传入ImageView的实际宽高(通过getMeasuredWidth()/getMeasuredHeight()获取),就能加载刚好适配控件的Bitmap,避免不必要的内存浪费。
3. 用成熟图片加载库(Glide/Picasso)
这类库会自动帮你处理所有内存优化细节:
- 智能内存/磁盘缓存
- 根据ImageView尺寸自动缩放图片
- 内存不足时自动清理缓存、降级加载
只需要几行代码就能替换原生加载逻辑,示例(Glide):
Glide.with(context) .load(R.drawable.your_image) .into(yourImageView);
这种方法最省心,而且库本身的体积增加远小于你复制多份图片的体积。
4. 压缩图片本身大小
用TinyPNG这类工具对原图进行无损/有损压缩,很多时候几百KB的图片能压缩到几十KB,视觉效果几乎无差异,同时也能降低加载时的内存占用。
总结
临时方案虽然解决了OOM,但体积暴涨会影响用户下载意愿。优先推荐图片加载库或手动控制采样率的方案,既能彻底解决OOM,又能保持应用体积在合理范围。如果是简单场景,把图片放到最高dpi目录也能快速解决问题。
内容的提问来源于stack exchange,提问作者Mr.BitCoin

