我的最终结论。
对于 API >= 29,在没有用户交互的情况下无法删除非拥有文件,并且无法绕过这一事实。
在Android 10/Q (API 29)中,必须捕获RecoverableSecurityException ,然后请求用户权限,最后如果被授予执行删除。
在Android 11/R (API 30)中得到了极大的改进。甚至可以在同一批次中合并已经拥有的文件,也可以批量删除。请求后无需处理任何事情,如果用户允许,系统会负责删除。
限制是它只处理媒体文件(图像、视频、音频)。对于其他文件类型,抛出 IllegalArgumentException并显示以下消息:“所有请求的项目必须由特定 ID 引用”,(请在 MediaStore 源代码中查看此消息)。
请注意,在 API 30 中有一个新的MANAGE_EXTERNAL_STORAGE权限,但它的使用需要在开发人员控制台中执行额外的步骤,例如解释为什么需要该权限。
例子:
public static void delete(final Activity activity, final Uri[] uriList, final int requestCode)
throws SecurityException, IntentSender.SendIntentException, IllegalArgumentException
{
final ContentResolver resolver = activity.getContentResolver();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
{
// WARNING: if the URI isn't a MediaStore Uri and specifically
// only for media files (images, videos, audio) then the request
// will throw an IllegalArgumentException, with the message:
// 'All requested items must be referenced by specific ID'
// No need to handle 'onActivityResult' callback, when the system returns
// from the user permission prompt the files will be already deleted.
// Multiple 'owned' and 'not-owned' files can be combined in the
// same batch request. The system will automatically delete them
// using the same prompt dialog, making the experience homogeneous.
final List<Uri> list = new ArrayList<>();
Collections.addAll(list, uriList);
final PendingIntent pendingIntent = MediaStore.createDeleteRequest(resolver, list);
activity.startIntentSenderForResult(pendingIntent.getIntentSender(), requestCode, null, 0, 0, 0, null);
}
else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q)
{
try
{
// In Android == Q a RecoverableSecurityException is thrown for not-owned.
// For a batch request the deletion will stop at the failed not-owned
// file, so you may want to restrict deletion in Android Q to only
// 1 file at a time, to make the experience less ugly.
// Fortunately this gets solved in Android R.
for (final Uri uri : uriList)
{
resolver.delete(uri, null, null);
}
}
catch (RecoverableSecurityException ex)
{
final IntentSender intent = ex.getUserAction()
.getActionIntent()
.getIntentSender();
// IMPORTANT: still need to perform the actual deletion
// as usual, so again getContentResolver().delete(...),
// in your 'onActivityResult' callback, as in Android Q
// all this extra code is necessary 'only' to get the permission,
// as the system doesn't perform any actual deletion at all.
// The onActivityResult doesn't have the target Uri, so you
// need to cache it somewhere.
activity.startIntentSenderForResult(intent, requestCode, null, 0, 0, 0, null);
}
}
else
{
// As usual for older APIs
for (final Uri uri : uriList)
{
resolver.delete(uri, null, null);
}
}
}