You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android 9下如何删除可移除SD卡(二级外部存储)中的文件?

在Android 9中删除外置SD卡文件的正确解决方案

我完全懂你折腾两天还搞不定的沮丧——Android对外置SD卡的权限限制确实挺绕的,尤其是从Android 4.4开始,第三方APP就失去了直接读写SD卡任意文件的权限,更别说Android 9了。咱们先拆解你之前操作的问题,再给你靠谱的解决办法:

为什么你之前的方法都失败了?

  1. 直接用File.delete():Android 9中,外置SD卡只有APP自己的私有目录(比如/storage/2585-1513/Android/data/你的包名/)允许直接用File类操作,其他目录(比如DCIM)必须通过**存储访问框架(SAF)**来处理。
  2. 错误使用SAF:你之前拿data.getData().getPath()拼接成路径转成File是完全错误的——SAF返回的Uri是content://开头的虚拟路径,不是真实的文件系统路径,所以new File(strFilePath)根本找不到文件,自然删不掉。

正确的SAF操作步骤(附代码)

要删除外置SD卡上的文件,必须通过DocumentFileContentResolver来操作SAF返回的目录Uri,步骤如下:

1. 启动文档树选择器,让用户选择SD卡根目录

private static final int REQUEST_PICK_SD_DIR = 123;

// 调用此方法打开SD卡选择对话框
private void selectSdCardDirectory() {
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
    // 可选:添加提示引导用户选择外置SD卡
    intent.putExtra("android.content.extra.SHOW_ADVANCED", true);
    startActivityForResult(intent, REQUEST_PICK_SD_DIR);
}

2. 处理返回结果,保存持久权限并删除目标文件

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    
    if (requestCode == REQUEST_PICK_SD_DIR && resultCode == Activity.RESULT_OK) {
        if (data == null) return;
        
        Uri treeUri = data.getData();
        if (treeUri == null) return;

        // 保存持久权限:让APP重启后依然能访问这个目录,不用重复请求用户
        getContentResolver().takePersistableUriPermission(
            treeUri,
            Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
        );

        // 通过DocumentFile获取SD卡根目录
        DocumentFile sdRootDir = DocumentFile.fromTreeUri(this, treeUri);
        if (sdRootDir == null || !sdRootDir.exists()) {
            Log.v("SAF_LOG", "SD卡根目录未找到");
            return;
        }

        // 逐层找到目标文件:DCIM/1.PDF
        DocumentFile dcimDir = sdRootDir.findFile("DCIM");
        if (dcimDir == null || !dcimDir.exists()) {
            Log.v("SAF_LOG", "DCIM目录未找到");
            return;
        }

        DocumentFile targetFile = dcimDir.findFile("1.PDF");
        if (targetFile != null && targetFile.exists()) {
            boolean isDeleted = targetFile.delete();
            if (isDeleted) {
                Log.v("SAF_LOG", "文件删除成功!");
            } else {
                Log.v("SAF_LOG", "文件删除失败");
            }
        } else {
            Log.v("SAF_LOG", "目标文件不存在");
        }
    }
}

进阶:跳过用户选择,直接用已知的SD卡Volume ID操作

如果你已经通过getVolumePaths()获取到了SD卡的Volume ID(比如你的2585-1513),可以直接构建对应的树Uri,不用每次让用户选择:

// 直接构建SD卡的树Uri
Uri sdCardTreeUri = DocumentsContract.buildTreeDocumentUri(
    "com.android.externalstorage.documents", 
    "2585-1513:"
);

// 先检查是否已有持久权限,没有的话再请求用户授权
if (!hasPersistablePermission(sdCardTreeUri)) {
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
    intent.setData(sdCardTreeUri);
    startActivityForResult(intent, REQUEST_PICK_SD_DIR);
} else {
    // 已有权限,直接操作文件
    DocumentFile sdRootDir = DocumentFile.fromTreeUri(this, sdCardTreeUri);
    // 后续删除步骤和上面一致
}

// 检查是否已有持久权限的工具方法
private boolean hasPersistablePermission(Uri uri) {
    int flags = getContentResolver().getPersistedUriPermissions().stream()
        .filter(perm -> perm.getUri().equals(uri))
        .findFirst()
        .map(UriPermission::getFlags)
        .orElse(0);
    return (flags & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION)) != 0;
}

关键注意事项

  • 必须请求用户授权:SAF的核心是用户主动授予APP访问特定目录的权限,不能绕过用户操作。
  • 持久权限很重要:takePersistableUriPermission能让APP在重启后依然保留权限,避免重复弹窗请求用户。
  • 永远不要用SAF的Uri转File路径:SAF的Uri是虚拟路径,只能通过DocumentFileContentResolver来操作。

内容的提问来源于stack exchange,提问作者MSeiz5

火山引擎 最新活动