从相册选择图片耗时后应用崩溃问题排查求助
Android相册选图偶发崩溃问题排查与修复
问题场景与现象
- 触发场景:打开相册选择图片,停留普通人查找图片的平均时长后返回应用,偶发崩溃
- 异常表现:本地日志无相关错误信息,疑似崩溃发生在应用外部
相关代码
图片选择触发代码
add_image.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(); intent.setAction(Intent.ACTION_GET_CONTENT); intent.setType("image/*"); startActivityForResult(Intent.createChooser(intent, "Select Picture"), PICK_IMAGE); } });
图片获取与压缩代码
@Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { if (requestCode == PICK_IMAGE && resultCode == RESULT_OK && data != null) { imageUri = data.getData(); imageView.setImageURI(imageUri); imageView.setVisibility(View.VISIBLE); isImageSelected = true; try { Bitmap original = MediaStore.Images.Media.getBitmap(getContentResolver(), imageUri); ByteArrayOutputStream stream = new ByteArrayOutputStream(); original.compress(Bitmap.CompressFormat.JPEG, UPLOAD_IMAGE_QUALITY, stream); image = stream.toByteArray(); } catch (IOException e) { e.printStackTrace(); } } super.onActivityResult(requestCode, resultCode, data); }
可能原因分析
- 应用被系统后台回收:当应用进入后台(相册前台运行),若系统内存紧张,会优先回收后台应用。此时从相册返回,应用重启后丢失
startActivityForResult的状态,触发异常。 - Uri临时权限过期:
ACTION_GET_CONTENT获取的Uri为临时权限,应用被回收重启后权限失效,调用MediaStore.Images.Media.getBitmap时触发未被妥善处理的IO异常。 - Bitmap内存溢出:直接加载原图到内存,高分辨率图片易触发OOM,部分OOM日志可能被系统截断或未被捕获。
修复方案
1. 处理应用后台回收后的状态恢复
在onSaveInstanceState中保存关键状态,onCreate中恢复,避免状态丢失:
@Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putBoolean("isSelectingImage", true); outState.putInt("pickRequestCode", PICK_IMAGE); if (imageUri != null) { outState.putString("selectedImageUri", imageUri.toString()); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.your_layout); if (savedInstanceState != null) { isImageSelected = savedInstanceState.getBoolean("isSelectingImage", false); String uriStr = savedInstanceState.getString("selectedImageUri"); if (uriStr != null) { imageUri = Uri.parse(uriStr); imageView.setImageURI(imageUri); imageView.setVisibility(View.VISIBLE); } } // 其他初始化逻辑 }
2. 延长Uri权限有效期
申请持久化Uri权限,避免应用重启后权限失效:
// 启动选择器时添加权限标记 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // onActivityResult中获取持久权限 if (requestCode == PICK_IMAGE && resultCode == RESULT_OK && data != null) { imageUri = data.getData(); getContentResolver().takePersistableUriPermission(imageUri, Intent.FLAG_GRANT_READ_URI_PERMISSION); // 后续逻辑... }
3. 优化Bitmap加载,避免OOM
通过采样缩放加载图片,而非直接加载原图:
private Bitmap decodeSampledBitmapFromUri(Uri uri, int reqWidth, int reqHeight) throws IOException { InputStream input = getContentResolver().openInputStream(uri); final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeStream(input, null, options); input.close(); options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); options.inJustDecodeBounds = false; input = getContentResolver().openInputStream(uri); Bitmap bitmap = BitmapFactory.decodeStream(input, null, options); input.close(); return bitmap; } 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; } // 在onActivityResult中替换原加载逻辑 try { int targetWidth = imageView.getWidth() == 0 ? 800 : imageView.getWidth(); int targetHeight = imageView.getHeight() == 0 ? 800 : imageView.getHeight(); Bitmap original = decodeSampledBitmapFromUri(imageUri, targetWidth, targetHeight); ByteArrayOutputStream stream = new ByteArrayOutputStream(); original.compress(Bitmap.CompressFormat.JPEG, UPLOAD_IMAGE_QUALITY, stream); image = stream.toByteArray(); original.recycle(); // 及时回收内存 } catch (IOException e) { e.printStackTrace(); Toast.makeText(this, "图片加载失败", Toast.LENGTH_SHORT).show(); }
4. 完善异常捕获范围
捕获OutOfMemoryError并处理,避免崩溃扩散:
try { // 图片加载与压缩逻辑 } catch (IOException e) { e.printStackTrace(); Toast.makeText(this, "图片加载失败", Toast.LENGTH_SHORT).show(); } catch (OutOfMemoryError error) { error.printStackTrace(); Toast.makeText(this, "内存不足,无法加载图片", Toast.LENGTH_SHORT).show(); if (imageView.getDrawable() instanceof BitmapDrawable) { BitmapDrawable drawable = (BitmapDrawable) imageView.getDrawable(); drawable.getBitmap().recycle(); } System.gc(); }
内容的提问来源于stack exchange,提问作者Bryne Phiri
相关产品推荐
相关产品推荐

