Android图片压缩应用调用compressToFile空指针异常问题求助
问题排查及解决方案
错误根因
- Compressor库的
compressToFile()方法要求传入原图片对应的File对象,你当前传入的imageBitmap是Bitmap类型,且没有对该变量做赋值操作,属于空对象 - 你注释掉了
originalImage对象的实例化代码,后续调用originalImage.getName()也会触发空指针 - 即使你恢复注释代码,直接用Uri的路径创建File对象在Android 10+分区存储规则下也无效,会得到空文件
onActivityResult的判断逻辑错误,把requestCode和resultCode混淆,无法正确区分相机/相册返回的结果
修复方案
第一步:声明全局变量
在Activity顶部添加以下全局变量:
private Uri originalImageUri; private File originalImageFile; private String originalFileName;
第二步:修改onActivityResult逻辑
修正判断逻辑,把Uri对应的文件复制到应用私有目录拿到可用的File对象:
@Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); // 修正判断逻辑:先判断返回结果成功,再匹配请求码是否为图片选择/拍照的自定义值 if (resultCode == RESULT_OK && requestCode == RESULT_IMAGE && data != null) { btnCompress.setVisibility(View.VISIBLE); originalImageUri = data.getData(); try { // 展示原图到控件 final InputStream imageStream = getContentResolver().openInputStream(originalImageUri); final Bitmap selectedImage = BitmapFactory.decodeStream(imageStream); imgOriginal.setImageBitmap(selectedImage); // 查询原文件的文件名 Cursor cursor = getContentResolver().query(originalImageUri, null, null, null, null); int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); cursor.moveToFirst(); originalFileName = cursor.getString(nameIndex); cursor.close(); // 展示原文件大小 long size = DocumentFile.fromSingleUri(this, originalImageUri).length(); txtOriginal.setText("Size: " + Formatter.formatShortFileSize(this, size)); // 将Uri对应的文件复制到应用私有缓存目录,生成可正常读写的File对象 originalImageFile = new File(getCacheDir(), originalFileName); FileOutputStream out = new FileOutputStream(originalImageFile); selectedImage.compress(Bitmap.CompressFormat.JPEG, 100, out); out.flush(); out.close(); } catch (Exception e) { e.printStackTrace(); Toast.makeText(this, "加载图片失败", Toast.LENGTH_SHORT).show(); } } else { Toast.makeText(this, "未选择图片", Toast.LENGTH_SHORT).show(); } }
第三步:修改压缩按钮点击逻辑
btnCompress.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // 增加非空校验避免空指针 if (originalImageFile == null || !originalImageFile.exists()) { Toast.makeText(MainActivity.this, "原文件不存在,请重新选择图片", Toast.LENGTH_SHORT).show(); return; } String widthStr = txtWidth.getText().toString(); String heightStr = txtHeight.getText().toString(); if (widthStr.isEmpty() || heightStr.isEmpty()) { Toast.makeText(MainActivity.this, "请填写宽高参数", Toast.LENGTH_SHORT).show(); return; } int quality = seekBar.getProgress(); int width = Integer.parseInt(widthStr); int height = Integer.parseInt(heightStr); try { // 传入正确的原文件File对象 File compressedImage = new Compressor(MainActivity.this) .setMaxWidth(width) .setMaxHeight(height) .setQuality(quality) .setCompressFormat(Bitmap.CompressFormat.JPEG) .setDestinationDirectoryPath(filepath) .compressToFile(originalImageFile); // 展示压缩后的图片和大小 Bitmap finalBitmap = BitmapFactory.decodeFile(compressedImage.getAbsolutePath()); imgCompressed.setImageBitmap(finalBitmap); txtCompressed.setText("Size: " + Formatter.formatShortFileSize(MainActivity.this, compressedImage.length())); // 存储逻辑说明: // 1. 保存为新文件:直接使用compressedImage的路径即可 // 2. 覆盖原文件:通过ContentResolver将compressedImage写入originalImageUri对应的系统媒体库路径即可 } catch (IOException e) { e.printStackTrace(); Toast.makeText(MainActivity.this, "压缩失败", Toast.LENGTH_SHORT).show(); } } });
注意事项
- 提前申请对应存储权限:Android 13+需要申请
READ_MEDIA_IMAGES权限,Android 10及以下需要申请WRITE_EXTERNAL_STORAGE权限 - 覆盖原文件时不能直接修改系统媒体库路径的文件,需要通过ContentResolver操作
- 如果需要保留EXIF信息,复制原文件到缓存目录时建议直接复制IO流而不是压缩Bitmap
内容的提问来源于stack exchange,提问作者flm
相关产品推荐
相关产品推荐

