You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从相册选择图片耗时后应用崩溃问题排查求助

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);

}

可能原因分析

  1. 应用被系统后台回收:当应用进入后台(相册前台运行),若系统内存紧张,会优先回收后台应用。此时从相册返回,应用重启后丢失startActivityForResult的状态,触发异常。
  2. Uri临时权限过期:ACTION_GET_CONTENT获取的Uri为临时权限,应用被回收重启后权限失效,调用MediaStore.Images.Media.getBitmap时触发未被妥善处理的IO异常。
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 22:57:38