Android 10 无法删除多个文件(范围存储)

标签 android kotlin android-10.0 scoped-storage

我一直在阅读 android 文档,但找不到 的解决方案在没有多个对话框的情况下删除多个文件 在 Android 10(API 级别 29)中
我在 Android 文档中找到了以下内容:

  • API 级别 < 29:删除没有对话框的文件,在应用启动时授予权限
  • API 级别 > 29:通过在用户必须确认的对话框中显示所有文件来删除文件
  • API 级别 = 29:通过显示每个文件的权限对话框来删除文件

  • 这不仅对我而且对每个 API 级别 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。我假设 Google 忘记在他们的 API 中实现此功能。但是,我希望有一个适当的解决方案,因为这会在使用 Android 10 时破坏应用程序。

    最佳答案

    实际上,您可以通过两种方式进行操作。
    1. 使用媒体存储:
    您可以像这样使用 Mediastore 删除 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。

    关于Android 10 无法删除多个文件(范围存储),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70318511/

    相关文章:

    android - Android Q 上 chris Jenx 的书法库崩溃

    android - 更改语言环境在 Android 10 中停止工作

    java - ScheduledThreadPoolExecutor Callable() 阻塞了我的主 Activity UI 线程

    java - Android中使用泰历解析特定格式的日期

    android - 如何在 Compose 中构建以下布局?

    spring - 如何在 Spring Boot 测试中指定 text/plain;charset=UTF-8 的媒体类型

    java - 如何在 Android Pie 中使用 Intent.ACTION_OPEN_DOCUMENT

    android - 保存的 JPEG 是旋转后从磁盘加载的 JPEG 的两倍大

    c# - 如何使对象/层忽略附近的裁剪平面?

    android - 重复的zip条目[okhttp-2.4.0.jar:com/squareup/okhttp/Address.class]