Android Java项目运行崩溃报java.lang.OutOfMemoryError,求解决方案
解决Android Bitmap加载导致的OutOfMemoryError问题
核心问题分析
从堆栈跟踪可以明确,OOM是在加载资源中的Bitmap时触发的——系统尝试分配近380MB内存来加载一张超大分辨率的图片,远超过当前可用内存。android:largeHeap="true"只是临时扩容,无法解决图片本身过大的根本问题。
具体解决方案
1. 检查并优化原始资源图片
- 定位问题图片:根据堆栈最后一行
TypedArray.getDrawable,排查布局文件、样式文件中引用的drawable(比如ImageView的src属性、主题背景等),找到对应的图片文件。 - 压缩图片分辨率:用图片编辑工具(如Photoshop、GIMP)将图片压缩到适配设备屏幕的尺寸(比如手机屏幕最大分辨率通常在1080x2340左右),避免加载远超显示需求的超大图。
- 放置到正确的资源目录:将图片放到匹配其分辨率的drawable目录(如xxhdpi/xxxhdpi),避免系统自动放大图片导致内存占用飙升。
2. 使用BitmapFactory.Options优化加载过程
手动加载图片时,通过采样缩小图片尺寸,同时调整像素格式减少内存占用:
// 获取屏幕尺寸 DisplayMetrics metrics = getResources().getDisplayMetrics(); int screenWidth = metrics.widthPixels; int screenHeight = metrics.heightPixels; BitmapFactory.Options options = new BitmapFactory.Options(); // 先获取图片原始尺寸,不加载到内存 options.inJustDecodeBounds = true; BitmapFactory.decodeResource(getResources(), R.drawable.target_image, options); // 计算采样率,确保加载后的图片尺寸不超过屏幕 int sampleSize = calculateSampleSize(options, screenWidth, screenHeight); // 开启实际加载 options.inJustDecodeBounds = false; options.inSampleSize = sampleSize; // 如果图片不需要透明通道,使用RGB_565格式,内存占用比ARGB_8888减少一半 options.inPreferredConfig = Bitmap.Config.RGB_565; // 加载优化后的Bitmap Bitmap optimizedBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.target_image, options);
辅助计算采样率的方法:
private int calculateSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { final int originalWidth = options.outWidth; final int originalHeight = options.outHeight; int sampleSize = 1; if (originalHeight > reqHeight || originalWidth > reqWidth) { final int halfHeight = originalHeight / 2; final int halfWidth = originalWidth / 2; // 找到最大的采样率,确保缩放后的图片仍能覆盖需求尺寸 while ((halfHeight / sampleSize) >= reqHeight && (halfWidth / sampleSize) >= reqWidth) { sampleSize *= 2; } } return sampleSize; }
3. 避免Bitmap内存泄漏
- 及时回收Bitmap:在Activity/Fragment销毁时,调用
bitmap.recycle()并将引用置为null,释放内存。 - 使用LruCache缓存Bitmap:复用已加载的Bitmap,避免重复分配内存:
// 设置缓存大小为应用最大可用内存的1/8 int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); int cacheSize = maxMemory / 8; LruCache<String, Bitmap> bitmapCache = new LruCache<String, Bitmap>(cacheSize) { @Override protected int sizeOf(String key, Bitmap bitmap) { // 返回Bitmap占用的KB数 return bitmap.getByteCount() / 1024; } }; // 存入缓存 bitmapCache.put("image_key", optimizedBitmap); // 从缓存获取 Bitmap cachedBitmap = bitmapCache.get("image_key");
4. 移除android:largeHeap="true"
这个配置只是临时扩容,会掩盖内存管理的根本问题,且不同设备的大堆内存限制不同,优化图片加载后即可移除。
内容的提问来源于stack exchange,提问作者martine
相关产品推荐
相关产品推荐

