调用MediaStore.getBitmap获取相机图片时触发内存不足错误求助
解决相机图片加载时的OutOfMemoryError问题
我太懂这个头疼的问题了!现在手机相机拍的照片分辨率动辄几千万像素,直接用MediaStore.Images.Media.getBitmap()加载整张图,内存根本扛不住——这个方法会把图片的完整像素数据一股脑塞进内存,触发OOM简直是必然的。
问题根源
MediaStore.Images.Media.getBitmap() 底层其实就是调用BitmapFactory.decodeStream(),但完全没做内存优化,直接加载原始尺寸的图片。比如一张4000×3000的ARGB_8888格式图片,内存占用就有4000×3000×4=48MB,要是App的内存配额不够,直接就炸了。
解决方案:用BitmapFactory.Options做采样缩放
我们可以通过BitmapFactory.Options加载缩小版的图片,只保留我们需要的尺寸,大幅降低内存占用。具体步骤如下:
- 先获取图片原始宽高,但不加载到内存(避免提前占内存)
- 根据ImageView的尺寸计算合适的采样率(采样率为2的整数倍,比如设为2,图片宽高就会变成原来的1/2,内存占用直接减为1/4)
- 用计算好的采样率加载缩小后的图片
另外还要修正你代码里的一个小bug:50 / 100是整数除法,结果是0,压缩质量应该直接传50(代表50%的压缩质量)。
修改后的完整代码
public void grabImage(ImageView imageView) { this.getContentResolver().notifyChange(mImageUri, null); ContentResolver cr = this.getContentResolver(); Bitmap bitmap = null; try { // 1. 仅获取图片原始尺寸,不加载到内存 BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; InputStream inputStream = cr.openInputStream(mImageUri); BitmapFactory.decodeStream(inputStream, null, options); inputStream.close(); // 2. 根据ImageView尺寸计算采样率 int reqWidth = imageView.getWidth(); int reqHeight = imageView.getHeight(); int inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // 3. 用采样率加载缩小后的图片 options.inJustDecodeBounds = false; options.inSampleSize = inSampleSize; options.inPreferredConfig = Bitmap.Config.RGB_565; // 用RGB_565替代ARGB_8888,内存减半 inputStream = cr.openInputStream(mImageUri); bitmap = BitmapFactory.decodeStream(inputStream, null, options); inputStream.close(); // 修复压缩参数错误:直接传50而非50/100 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 50, byteArrayOutputStream); getImageUri(this, bitmap); imageView.setImageBitmap(bitmap); } catch (IOException e) { Toast.makeText(this, "Failed to load", Toast.LENGTH_SHORT).show(); Log.d(TAG, "Failed to load", e); } catch (OutOfMemoryError e) { e.printStackTrace(); Toast.makeText(this, "内存不足,无法加载图片", Toast.LENGTH_SHORT).show(); } finally { // 可选:当图片不再使用时,及时回收释放内存 // if (bitmap != null && !bitmap.isRecycled()) { // bitmap.recycle(); // } } } // 计算合适的采样率工具方法 private 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; }
额外优化小技巧
- 优先用
Bitmap.Config.RGB_565:除非你需要透明通道,否则这个格式每个像素只占2字节,比默认的ARGB_8888省一半内存。 - 及时回收Bitmap:当图片不再显示时,调用
bitmap.recycle()释放内存,注意别在还在使用Bitmap的时候调用。 - 考虑用Glide/Picasso等图片库:这些库已经封装了完善的内存优化、缓存机制,能更省心地处理图片加载,从根源上避免OOM。
内容的提问来源于stack exchange,提问作者Developer
相关产品推荐
相关产品推荐

