使用Activity Result API从图库获取多图时触发java.lang.OutOfMemoryError,如何压缩图片?
解决Activity Result API选择多张图片时的OutOfMemoryError问题
这个问题确实很常见——直接加载高分辨率的原图很容易把内存撑爆,尤其是一次性加载多张的时候。咱们可以通过图片采样压缩来解决,在把图片显示到ImageView之前先缩小它的尺寸,减少内存占用。下面是具体的修改方案:
1. 编写图片压缩工具方法
首先添加一个用于从Uri获取压缩后Bitmap的工具方法,利用BitmapFactory.Options的inSampleSize参数来降低图片分辨率:
private Bitmap getResizedBitmapFromUri(Context context, Uri uri, int targetWidth, int targetHeight) { BitmapFactory.Options options = new BitmapFactory.Options(); // 先只获取图片的宽高信息,不加载整个Bitmap到内存 options.inJustDecodeBounds = true; try { InputStream inputStream = context.getContentResolver().openInputStream(uri); BitmapFactory.decodeStream(inputStream, null, options); inputStream.close(); } catch (IOException e) { e.printStackTrace(); return null; } // 计算合适的采样率:把图片宽高缩小到目标尺寸以内 int width = options.outWidth; int height = options.outHeight; int inSampleSize = 1; if (height > targetHeight || width > targetWidth) { final int halfHeight = height / 2; final int halfWidth = width / 2; // 找到最大的inSampleSize,使得缩小后的图片宽高仍大于等于目标尺寸 while ((halfHeight / inSampleSize) >= targetHeight && (halfWidth / inSampleSize) >= targetWidth) { inSampleSize *= 2; } } // 现在加载压缩后的Bitmap options.inJustDecodeBounds = false; options.inSampleSize = inSampleSize; options.inPreferredConfig = Bitmap.Config.RGB_565; // 进一步减少内存占用(每个像素2字节,ARGB_8888是4字节) try { InputStream inputStream = context.getContentResolver().openInputStream(uri); Bitmap resizedBitmap = BitmapFactory.decodeStream(inputStream, null, options); inputStream.close(); return resizedBitmap; } catch (IOException e) { e.printStackTrace(); return null; } }
2. 修改Activity Result回调逻辑
在onActivityResult中,不再直接用setImageURI加载原图,而是调用上面的压缩方法获取小尺寸Bitmap后再设置给ImageView:
ActivityResultLauncher<String> launchImage= registerForActivityResult( new ActivityResultContracts.GetMultipleContents(), new ActivityResultCallback<List<Uri>>() { @Override public void onActivityResult(List<Uri> uriList) { try { // 定义目标ImageView的尺寸(和你设置的LayoutParams一致:600x800) final int targetWidth = 600; final int targetHeight = 800; for (Uri uri : uriList) { ImageView imageView = new ImageView(MainActivity.this); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(targetWidth, targetHeight); lp.setMargins(0, 50, 0, 0); imageView.setLayoutParams(lp); // 获取压缩后的Bitmap Bitmap resizedBitmap = getResizedBitmapFromUri(MainActivity.this, uri, targetWidth, targetHeight); if (resizedBitmap != null) { imageView.setImageBitmap(resizedBitmap); } else { // 加载失败时的 fallback,比如显示默认占位图 imageView.setImageResource(R.drawable.default_image); } ll.addView(imageView); } } catch (Exception e) { e.printStackTrace(); } } });
3. 额外优化建议
- 使用图片加载库:如果不想自己处理压缩和内存管理,推荐用Glide、Picasso这类成熟的图片加载库,它们会自动处理图片压缩、内存缓存、磁盘缓存,代码更简洁:
比如用Glide的话,只需要一行代码:Glide.with(MainActivity.this) .load(uri) .override(600, 800) // 指定目标尺寸 .into(imageView); - 回收Bitmap:如果Activity销毁或者不再需要这些图片时,可以遍历ImageView回收Bitmap,避免内存泄漏:
@Override protected void onDestroy() { super.onDestroy(); for (int i = 0; i < ll.getChildCount(); i++) { View child = ll.getChildAt(i); if (child instanceof ImageView) { Bitmap bitmap = ((ImageView) child).getDrawingCache(); if (bitmap != null && !bitmap.isRecycled()) { bitmap.recycle(); } } } }
内容的提问来源于stack exchange,提问作者steady blaze
相关产品推荐
相关产品推荐

