Android 9下如何删除可移除SD卡(二级外部存储)中的文件?
在Android 9中删除外置SD卡文件的正确解决方案
我完全懂你折腾两天还搞不定的沮丧——Android对外置SD卡的权限限制确实挺绕的,尤其是从Android 4.4开始,第三方APP就失去了直接读写SD卡任意文件的权限,更别说Android 9了。咱们先拆解你之前操作的问题,再给你靠谱的解决办法:
为什么你之前的方法都失败了?
- 直接用
File.delete():Android 9中,外置SD卡只有APP自己的私有目录(比如/storage/2585-1513/Android/data/你的包名/)允许直接用File类操作,其他目录(比如DCIM)必须通过**存储访问框架(SAF)**来处理。 - 错误使用SAF:你之前拿
data.getData().getPath()拼接成路径转成File是完全错误的——SAF返回的Uri是content://开头的虚拟路径,不是真实的文件系统路径,所以new File(strFilePath)根本找不到文件,自然删不掉。
正确的SAF操作步骤(附代码)
要删除外置SD卡上的文件,必须通过DocumentFile和ContentResolver来操作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是虚拟路径,只能通过
DocumentFile或ContentResolver来操作。
内容的提问来源于stack exchange,提问作者MSeiz5




