如何读取Android可移动SD卡文件?getExternalStorageDirectory替代方案
Environment.getExternalStorageDirectory() 本身从设计之初就只指向设备主外部存储(即内置共享存储分区),从来没有支持过可移动SD卡访问,它被废弃是因为Android 10之后引入了分区存储,直接通过File路径遍历全量公共文件的方式不再被推荐,和SD卡访问能力无关。
你已经配置了存储权限的前提下,可以根据自己的需求选以下方案实现:
方案1:用MediaStore API读取所有音频文件(最推荐,适配全版本)
如果你只需要读取设备上的音频文件,不需要遍历SD卡上所有类型的文件,直接用系统提供的MediaStore接口查询即可,系统会自动汇总内置存储、可移动SD卡上的所有合规音频文件,不需要你手动处理不同设备的SD卡挂载点差异,也不需要额外申请特殊权限。
示例代码(Java):
ArrayList<File> mySongs = new ArrayList<>(); // 查询音频媒体库 Uri audioUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; String[] projection = new String[]{MediaStore.Audio.Media.DATA}; // 过滤只保留音乐文件,排除铃声、录音等短音频 String selection = MediaStore.Audio.Media.IS_MUSIC + " = 1"; Cursor cursor = getContentResolver().query(audioUri, projection, selection, null, null); if (cursor != null) { int pathIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA); while (cursor.moveToNext()) { String filePath = cursor.getString(pathIndex); mySongs.add(new File(filePath)); } cursor.close(); }
注意Android 13及以上版本,需要在Manifest中替换原来的READ_EXTERNAL_STORAGE权限为READ_MEDIA_AUDIO,否则查询会返回空。
方案2:获取可移动SD卡根路径(仅适合低版本或访问专属目录)
如果你确实需要拿到SD卡的根路径,可以通过getExternalFilesDirs()方法(注意是带s的复数方法)获取所有外部存储上的应用专属目录,从中提取可移动存储的根路径:
File[] externalStorageDirs = getExternalFilesDirs(null); File sdCardRoot = null; for (File dir : externalStorageDirs) { // 判断是否为可移动存储 if (dir != null && Environment.isExternalStorageRemovable(dir)) { // 从应用专属路径截取到SD卡根路径 String sdCardPath = dir.getAbsolutePath().split("/Android")[0]; sdCardRoot = new File(sdCardPath); break; } }
注意:
- Android 10及以下版本,配合存储权限可以遍历该根路径下的所有文件
- Android 11及以上版本,你只能访问SD卡上的应用专属目录,以及通过MediaStore访问媒体文件,直接遍历根目录其他文件会抛出权限异常,没有绕过方法
- 绝对不要硬编码SD卡挂载路径,不同厂商、不同系统版本的挂载点规则差异极大,硬编码会在大量设备上失效
方案3:通过存储访问框架(SAF)访问SD卡全量文件
如果你需要访问SD卡上的非媒体类任意文件,可以使用系统提供的存储访问框架,调用系统文档选择器让用户手动授权SD卡根目录的访问权限,拿到权限后即可通过ContentResolver读写、遍历SD卡上的所有文件,该方案从Android 5.0开始支持,是官方认可的全量访问可移动存储的实现方式,不会触发应用商店审核风险。
避坑提示
- 不要滥用
MANAGE_EXTERNAL_STORAGE全文件访问权限,非文件管理器类应用申请该权限会被Google Play等主流应用商店拒绝上架 Environment.getExternalStoragePublicDirectory()同样在Android 10被废弃,仅在开启requestLegacyExternalStorage兼容模式的应用中可以临时使用,不推荐作为长期方案
内容的提问来源于stack exchange,提问作者Abdul Hannan

