适配最新系统的Android端Picture文件夹重复图库图片删除实现
方案一(推荐):从根源避免生成公共图库的冗余文件
你遇到的重复文件问题,本质上是调用了MediaStore.Images.Media.insertImage()把旋转后的图直接写入了系统公共图库目录,这个行为本身就会让图片被媒体扫描器捕获,出现在用户的相册列表里。
其实完全没必要把临时旋转的图片写到公共存储,直接利用应用专属的私有目录存临时文件,就能彻底绕开冗余文件和高版本系统的存储权限限制:
- 应用专属外部缓存目录的文件,系统媒体扫描器默认不会扫描,不会出现在用户的系统图库中,从根源上就不会有“重复图片被用户看到”的问题
- 访问、删除该目录下的文件,不需要申请任何存储权限,从Android 7.0到Android 14全版本兼容
- 该路径下的文件可以直接用来做Firebase Storage上传,不需要把文件转成公开的MediaStore Uri
具体改造后的旋转处理逻辑:
public Uri rotatedImage(Bitmap bitmap, Uri originalPhotoUri) { ExifInterface exifInterface = null; try (InputStream exifIs = getContentResolver().openInputStream(originalPhotoUri)) { // 高版本系统下直接传文件路径读Exif会触发FileNotFoundException,通过流读取适配所有Uri类型 exifInterface = new ExifInterface(exifIs); } catch (IOException e) { e.printStackTrace(); } int orientation = exifInterface.getAttributeInt( ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED ); Matrix matrix = new Matrix(); switch (orientation) { case ExifInterface.ORIENTATION_ROTATE_90: matrix.setRotate(90); break; case ExifInterface.ORIENTATION_ROTATE_180: matrix.setRotate(180); break; case ExifInterface.ORIENTATION_ROTATE_270: matrix.setRotate(270); break; default: } Bitmap rotatedBitmap = Bitmap.createBitmap( bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true ); // 把旋转后的图直接写到应用专属缓存目录,不触碰公共存储 File rotatedTempFile = new File( getExternalCacheDir(), "temp_rotated_" + System.currentTimeMillis() + ".jpg" ); try (FileOutputStream fos = new FileOutputStream(rotatedTempFile)) { rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos); fos.flush(); } catch (IOException e) { e.printStackTrace(); } // 及时回收bitmap内存,避免OOM if (!rotatedBitmap.equals(bitmap)) { rotatedBitmap.recycle(); bitmap.recycle(); } // 上传完成后直接调用rotatedTempFile.delete()即可,因为是自己目录下的文件,100%删除成功 return Uri.fromFile(rotatedTempFile); }
对应的onActivityResult逻辑也需要同步调整:不要通过PathUtil.getRealPathFromURI拿真实文件路径——Android 10之后很多图库返回的Uri根本没有对应公共存储上的真实文件路径,这个老方法在高版本上本身就会失效,直接把原始的data.getData()传给旋转方法即可。
方案二:如果保留现有MediaStore写入逻辑,适配高版本系统的删除方案
如果因为历史依赖没法改成私有目录存储,之前你试过的所有老删除方法失效的核心原因是:Android 10之后Scoped Storage生效,应用没有权限直接通过文件路径删除公共存储下的文件,必须通过MediaStore的标准接口做精准删除,而且这个过程不需要申请额外的存储权限。
正确的删除实现逻辑:
// 在Firebase上传任务的成功回调中调用,传入之前insertImage返回的Uri private void deleteInsertedMediaFile(Uri insertedImageUri) { try { // 从insertImage返回的Uri中解析出对应MediaStore条目的唯一ID long mediaId = ContentUris.parseId(insertedImageUri); // 构造精准匹配的删除目标Uri Uri targetUri = ContentUris.withAppendedId( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, mediaId ); // 直接通过ContentResolver删除,应用自己插入的MediaStore条目不需要额外权限 int deletedRows = getContentResolver().delete(targetUri, null, null); } catch (Exception e) { e.printStackTrace(); } }
这个方案的兼容性说明:
- 适配Android 10到Android 14全版本,不需要申请
WRITE_EXTERNAL_STORAGE或者MANAGE_EXTERNAL_STORAGE权限,完全符合Google Play的上架规则 - 不像老教程教的“遍历MediaStore找最近的文件删除”——那个方法在Android 13之后因为应用对MediaStore的查询权限被收紧,根本拿不到其他应用写入的图片条目,不仅删不掉,还容易误删其他APP的图片
- 因为是直接通过自己insertImage返回的Uri解析ID,不会出现匹配错文件的问题,删除准确率100%
内容的提问来源于stack exchange,提问作者Zoio
相关产品推荐
相关产品推荐

