如何删除我在Android Q 中创建的文件?

How to delete file created by me in Android Q?

我正在 DCIM 目录中保存图像,但在某些情况下,我需要将其删除。 以前,我只调用了 image.delete(),其中图像是文件。但现在这张图片以另一种方式保存:

    contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, name);
    contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/png");
    contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, "DCIM" + File.separator + IMAGES_FOLDER_NAME);

    Uri imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
    OutputStream fos = resolver.openOutputStream(imageUri);
    boolean saved = bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);

我尝试使用它的名称进行查询并调用 contentResolver.delete(...),但它不起作用。

我有写入外部存储的权限,但我不想使用 SAF。

如何删除这样的文件?

您需要使用调用插入时获得的 Uri ContentResolverdelete 方法。

 Uri imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
 OutputStream fos = resolver.openOutputStream(imageUri);
 boolean saved = bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
 ......
 int result = resolver.delete(imageUri, null, null);
 if (result > 0) {
     Log.d("Tag", "File deleted");
 }

如果您没有存储 Uri,则需要执行 query(),检索内容,然后调用 delete

  File file = new File(delPath);
  if (file.exists()) {
   try {
        Uri imageUri = FileProvider.getUriForFile(Context,
                        getApplicationContext()
                                .getPackageName() + ".provider", file);
        ContentResolver contentResolver = getContentResolver();
                int deletefile = contentResolver.delete(imageUri, null, null);
            } catch (Exception e) {
                e.printStackTrace();
            }

        }

这里的删除路径delPath是你要从存储中删除的图片的文件路径。

完整的解决方案还应包括处理在尝试使用 ContentResolver 删除时 Andorid Q 上可能发生的潜在错误。在这种情况下,您应该将代码包装在 try/catch 块中。 以下解决方案:

      try {
        // 1
        getApplication<Application>().contentResolver.delete(
                imageUri,"${MediaStore.Images.Media._ID} = ?",
                arrayOf(imageId)
        )
      }
       // 2
      catch (securityException: SecurityException) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
          val recoverableSecurityException =
                  securityException as? RecoverableSecurityException
                          ?: throw securityException

             val intentSender = recoverableSecurityException.userAction.actionIntent.intentSender
             startIntentSenderForResult(
                intentSender,
                DELETE_PERMISSION_REQUEST,
                null,
                0,
                0,
                0,
                null
              )
        } else {
          throw securityException
        }
      }
  1. 在这里,您在 try 块内调用 contentResolver.delete(),因为此方法会在运行时抛出 SecurityException。该方法需要您要删除的图像的 ContentUri。在 where 参数中,您指定要根据图像的 _ID 删除图像。在最后一个参数中,您在数组中传递 _ID 的字符串版本。

  2. 在 Android 10 及更高版本中,无法直接从 MediaStore 中删除或修改项目。您需要获得这些操作的许可。正确的做法是先捕获RecoverableSecurityException,它包含一个intentSender,可以提示用户授予权限。然后,您使用从 RecoverableSecurityException 中提取的 intentSender 启动 IntentSenderForResult,以授予在 Android Q.

  3. 上删除文件的额外权限

来源:https://www.raywenderlich.com/9577211-scoped-storage-in-android-10-getting-started

intent.addFlags 在调用方法 startActivityForResult 之前。 像这样

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
      intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
          | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

因为它在 ContentProvider#enforceWritePermissionInner 中检查权限。

真正的实现在 ActivityManagerService#checkPermission 方法中。