ImageView设置图片触发OutOfMemoryError问题求解
解决ImageView设置大图片触发OutOfMemoryError的问题
这个问题很典型——你遇到的OutOfMemoryError是因为要加载的图片太大了,从错误日志里能看到系统尝试分配715MB的内存,这显然远远超过了App能获取的内存上限。直接用setImageResource()会把整张图片解码后加载到内存,哪怕你的ImageView尺寸很小,也会加载原图的全部像素,最终导致内存溢出。
先排查核心原因
先确认R.drawable.aresbkg这张JPG的实际尺寸:按错误里的内存计算,715MB对应约1.86亿像素(每个ARGB_8888格式像素占4字节),差不多是13600×13600的超大图,这种尺寸完全不适合直接在Android应用里加载。之前其他应用没出现问题,大概率是那些应用用的图片尺寸都在合理范围内。
解决方案推荐
下面按从手动到自动的顺序,给你几种可行的解决方法:
1. 手动压缩加载图片(自定义逻辑)
通过BitmapFactory.Options来采样压缩,只加载和ImageView尺寸匹配的像素:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); button = findViewById(R.id.button); immagine = findViewById(R.id.imageView2); // 获取ImageView的实际显示尺寸(onCreate中直接获取可能为0,用post延迟获取) immagine.post(() -> { int targetWidth = immagine.getWidth(); int targetHeight = immagine.getHeight(); // 第一步:只获取图片原始尺寸,不加载到内存 BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(getResources(), R.drawable.aresbkg, options); int originalWidth = options.outWidth; int originalHeight = options.outHeight; // 计算采样率:找到最大的2的倍数,让采样后的尺寸不超过目标尺寸 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; } } // 第二步:加载压缩后的图片 options.inJustDecodeBounds = false; options.inSampleSize = inSampleSize; options.inPreferredConfig = Bitmap.Config.RGB_565; // 无透明需求时用,省一半内存 Bitmap compressedBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.aresbkg, options); immagine.setImageBitmap(compressedBitmap); }); }
- 关键点:
inJustDecodeBounds=true时不会加载Bitmap,只是读取尺寸;inSampleSize是采样比例,设为2就是宽高各缩为1/2,内存占用是原图的1/4;RGB_565格式比默认的ARGB_8888省一半内存(适合无透明通道的图片)。
2. 使用图片加载库(更省心的方案)
用Glide、Picasso这类成熟的图片加载库,它们会自动处理图片压缩、内存缓存、磁盘缓存,完全不用自己写复杂逻辑:
比如用Glide(需要先在build.gradle里添加依赖):
// 先添加依赖:implementation 'com.github.bumptech.glide:glide:4.16.0' // 然后在代码里一行搞定 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); button = findViewById(R.id.button); immagine = findViewById(R.id.imageView2); Glide.with(this).load(R.drawable.aresbkg).into(immagine); }
Glide会自动根据ImageView的尺寸压缩图片,还会智能管理内存,避免OOM,这是大多数Android开发者的首选方案。
3. 其他优化细节
- 把图片放在正确的drawable目录:比如高分辨率图片放在
drawable-xxhdpi,不要放在默认的drawable(会被系统按mdpi缩放,导致内存占用变大)。 - 手动回收Bitmap(如果是自己加载的):在Activity销毁时回收内存,避免泄漏:
@Override protected void onDestroy() { super.onDestroy(); Drawable drawable = immagine.getDrawable(); if (drawable instanceof BitmapDrawable) { Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap(); if (bitmap != null && !bitmap.isRecycled()) { bitmap.recycle(); } } }
内容的提问来源于stack exchange,提问作者Twing90
相关产品推荐
相关产品推荐

