Android 10不支持删除多个文件(Scoped Storage)

4
我一直在阅读Android文档,但我找不到一个解决方案来在Android 10(API Level 29)中删除多个文件而不弹出多个对话框
我在Android文档中找到了以下内容:
  • API Level < 29:无需对话框即可删除文件,应用启动时授予权限
  • API Level > 29:通过显示所有文件的对话框来删除文件,用户必须确认
  • API Level = 29:通过为每个文件显示权限对话框来删除文件
这不仅令我非常沮丧,对于每个使用API level 29的用户来说也是如此,因为有些情况下需要删除多达100个以上的文件。
想要删除媒体文件时的入口点。
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> {
    deleteMediaR(activity, uris)
}
Build.VERSION.SDK_INT == Build.VERSION_CODES.Q -> {
    deleteMediaQ(activity, uris)
}
else -> {
    deleteMediaDefault(activity, uris)
}

API级别29及以上

@RequiresApi(Build.VERSION_CODES.R)
private fun deleteMediaR(activity: Activity, uris: ArrayList<String>) {
    val contentResolver = activity.contentResolver
    val collection: ArrayList<Uri> = ArrayList()
    collection.addAll(uris.map { uri -> Uri.parse(uri) })
    val pendingIntent = MediaStore.createDeleteRequest(contentResolver, collection)
    activity.startIntentSenderForResult(
        pendingIntent.intentSender, 42, null, 0, 0, 0, null)
}

API级别29

@RequiresApi(Build.VERSION_CODES.Q)
private fun deleteMediaQ(activity: Activity, uris: ArrayList<String>) {
    try {
        deleteMediaDefault(activity, uris)
    } catch (exception: Exception) {
        if (exception is RecoverableSecurityException) {
            val pendingIntent: PendingIntent = exception.userAction.actionIntent
            activity.startIntentSenderForResult(pendingIntent.intentSender,
                42, null, 0, 0, 0, null)
        }
    }
}

低于 API 级别 29

contentResolver.delete(uri, where, media))

这是一个仅涉及 Android 10 的非常令人沮丧的问题。我认为谷歌忘记在他们的 API 中实现此功能。然而,我希望有适当的解决方案,因为使用 Android 10 时会破坏应用程序。


不要使用媒体存储库删除文件。只需使用经典的File类即可。您可以不使用对话框。 - blackapps
谢谢您的回复!我必须使用Media Store,因为我想读取/删除图片和视频。 一个很好的解决方法似乎是使用requestLegacyExternalStorage并保留preserveLegacyExternalStorage。 看起来Google已经意识到这个问题,并仍然支持传统的方法。 - Rehcan
我必须使用媒体存储,因为我想要读取/删除图片和视频。胡说八道。你可以使用Java IO删除任何文件。 - blackapps
1个回答

2

实际上,您可以通过两种方式来完成。

1. 使用媒体库:

您可以使用媒体库删除API轻松删除任何媒体文件,就像这样:

ActivityResultLauncher<IntentSenderRequest> deleteLauncher
            = registerForActivityResult(new ActivityResultContracts.StartIntentSenderForResult(),
            new ActivityResultCallback<ActivityResult>() {
                @Override
                public void onActivityResult(ActivityResult result) {
                    if (result.getResultCode() == RESULT_OK) {
                        // Media files deleted successfully. Do your stuff.
                    }
                }
            });


@RequiresApi(api = Build.VERSION_CODES.R)
    private void deleteAPI30(ArrayList<Media> mediaList, Context context) throws IntentSender.SendIntentException {
        ContentResolver contentResolver = context.getContentResolver();
        List<Uri> uriList = new ArrayList<>();
        for (int i = 0; i < mediaList.size(); i++) {
            uriList.add(mediaList.get(i).getAppendedIdUri());
        }
        Collections.addAll(uriList);
        PendingIntent pendingIntent = MediaStore.createDeleteRequest(contentResolver, uriList);
        IntentSenderRequest senderRequest = new IntentSenderRequest.Builder(pendingIntent.getIntentSender())
                .setFillInIntent(null)
                .setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION, 0)
                .build();
        deleteLauncher.launch(senderRequest);
    }

如果事情不容易完成,您需要使用SAF目录选择器获取目录访问权限。

2. 使用存储访问框架(SAF)删除:

public void deleteMediaSAF(ArrayList<Media> mediaList) {
        DocumentFile documentFile = DocumentFile.fromTreeUri(this, getContentResolver().getPersistedUriPermissions().get(0).getUri());
        for (int i = 0; i < mediaList.size(); i++) {
            File file = new File(mediaList.get(i).getPath());
            DocumentFile nextDocument = documentFile.findFile(file.getName());
            try {
                DocumentsContract.deleteDocument(getContentResolver(), nextDocument.getUri());
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }

        mediaList = loadMedia(DIR_NAME);
        adapter.setMediaList(mediaList);
        adapter.notifyDataSetChanged();
        adapter.deselectAll();
        if (mediaList.size() > 0) {
            hideEmptyView();
        } else {
            showEmptyView();
            menuItem.setVisible(false);
        }
    }

如果可能的话,您可以同时使用这些方法来处理不同API。例如,在Android 11及以上版本中使用MediaStore API,在Android 10中使用SAF API。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接