如何获取手机SD卡或内置存储中指定文件夹的路径?
搞定Android读取指定文件夹(内置/SD卡)的问题
嘿,我懂你遇到的麻烦了——之前用Environment.getExternalStorageDirectory()的方法,在高版本Android上可能不好使,尤其是SD卡的访问,现在Google的存储规则变了嘛。下面给你分情况讲清楚怎么适配:
一、为啥旧方法会失效?
从Android 10(API 29)开始,谷歌搞了个Scoped Storage(分区存储),原来的Environment.getExternalStorageDirectory()被标成废弃了,而且直接硬怼外部存储路径会被限制,SD卡的访问权限管得更严,不同品牌手机的SD卡路径还不一样,很容易踩坑。
二、分版本适配方案
1. Android 10及以上(必看!)
如果你的应用目标SDK是30+,推荐用**SAF(存储访问框架)**来让用户自己选文件夹——这是最合规的方式,还不用纠结SD卡路径的问题。步骤很简单:
- 先启动文件夹选择器:
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); startActivityForResult(intent, REQUEST_CODE_PICK_FOLDER); // 要是用AndroidX的话,建议用registerForActivityResult替代startActivityForResult
- 然后在回调里拿到用户选的文件夹Uri,还能把权限持久化,下次打开不用再选:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_CODE_PICK_FOLDER && resultCode == RESULT_OK) { Uri treeUri = data.getData(); // 把权限存下来,下次启动直接用 getContentResolver().takePersistableUriPermission(treeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION); DocumentFile selectedFolder = DocumentFile.fromTreeUri(this, treeUri); // 遍历文件夹里的文件 for (DocumentFile file : selectedFolder.listFiles()) { String fileName = file.getName(); Uri fileUri = file.getUri(); // 这里就可以处理你的文件啦 } } }
这个方法的好处是:不管是内置存储还是SD卡的自定义文件夹都能访问,完全符合高版本Android的权限要求,不会被系统限制。
2. Android 9及以下
低版本还能继续用旧的File API,但SD卡的路径得找对:
- 内置存储的"myfolder":还是用
new File(Environment.getExternalStorageDirectory(), "myfolder")就行 - SD卡的路径:别硬编码!用
getExternalFilesDirs()来获取所有外部存储目录:
File[] externalDirs = getExternalFilesDirs(null); // externalDirs[0]是内置存储的应用专属目录,externalDirs[1]存在的话就是SD卡的对应目录 // 要访问SD卡根目录下的myfolder,得往上跳两级目录 for (File dir : externalDirs) { if (dir != null && !dir.equals(getExternalFilesDir(null))) { File sdFolder = new File(dir.getParentFile().getParentFile(), "myfolder"); if (sdFolder.exists() && sdFolder.isDirectory()) { // 找到SD卡上的目标文件夹啦,开始处理文件 } } }
注意:这种方法在Android 10+会因为分区存储的限制失效,所以只适合低版本适配。
三、统一适配小技巧
如果你的应用要覆盖多个版本,加个版本判断就行:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // 用SAF的方式 } else { // 用旧的File API方式 }
另外,要是你暂时不想改SAF,在AndroidManifest里加android:requestLegacyExternalStorage="true"(仅针对API 29)能暂时绕过分区存储,但这只是过渡方案,谷歌未来可能会禁用,不建议长期用哦。
内容的提问来源于stack exchange,提问作者Neph




