Android 14原生Java项目如何获取SD卡全目录及所有文件?
解决方案:Android 14 遍历SD卡所有文件目录
问题1:目录获取错误的解决
在Android 14(API 34)中,Environment.getExternalStorageDirectory()返回的是内部共享存储根目录(即你看到的sdk_gphone64_x86_64相关路径),而非可移动SD卡目录。要访问可移动SD卡,需通过StorageManagerAPI实现:
步骤1:声明权限
在AndroidManifest.xml中添加权限配置:
<!-- Android 10+ 访问全外部存储的特殊权限 --> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" android:minSdkVersion="30" /> <!-- Android 10以下兼容读取权限 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
步骤2:动态申请特殊权限
MANAGE_EXTERNAL_STORAGE属于系统特殊权限,需引导用户到设置页授权:
private void requestStoragePermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (!Environment.isExternalStorageManager()) { Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION); Uri uri = Uri.fromParts("package", getPackageName(), null); intent.setData(uri); startActivityForResult(intent, 100); } else { scanAllStorageVolumes(); } } else { if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 200); } else { scanAllStorageVolumes(); } } } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == 100) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && Environment.isExternalStorageManager()) { scanAllStorageVolumes(); } else { Log.e("##PERMISSION", "用户未授予全存储访问权限"); } } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == 200 && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { scanAllStorageVolumes(); } else { Log.e("##PERMISSION", "用户未授予读取存储权限"); } }
步骤3:获取所有存储卷(含可移动SD卡)
通过StorageManager枚举所有可用存储卷,区分内部共享存储和可移动SD卡:
private void scanAllStorageVolumes() { StorageManager storageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE); List<StorageVolume> storageVolumes = storageManager.getStorageVolumes(); for (StorageVolume volume : storageVolumes) { boolean isRemovable = volume.isRemovable(); Uri volumeUri = volume.getDirectory(); File volumeDir = new File(volumeUri.getPath()); Log.e("##STORAGE", (isRemovable ? "可移动SD卡" : "内部共享存储") + "根目录:" + volumeDir.getAbsolutePath()); logFilesRecursively(volumeDir); } }
问题2:仅遍历当前目录的解决
原代码仅遍历当前目录的文件/子目录,未递归进入子目录。修改为递归遍历逻辑即可:
private void logFilesRecursively(File directory) { if (directory == null || !directory.exists() || !directory.isDirectory()) { return; } File[] files = directory.listFiles(); if (files == null) { Log.e("##FILE_INFO", "无法访问目录:" + directory.getAbsolutePath()); return; } for (File file : files) { Log.e("##FILE_INFO", (file.isDirectory() ? "[目录]" : "[文件]") + " 名称:" + file.getName() + ",路径:" + file.getAbsolutePath()); // 递归遍历子目录 if (file.isDirectory()) { logFilesRecursively(file); } } }
修复原代码小bug
原logFilesInPodcasts方法注释与代码不一致,修正为获取系统标准Podcasts目录:
private void logFilesInPodcasts() { File podcastsFolder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PODCASTS); Log.e("##PODCASTS", "Podcasts目录:" + podcastsFolder.getAbsolutePath()); logFilesRecursively(podcastsFolder); }
完整Activity代码
public class LogFileListActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_log_file_list); requestStoragePermission(); } private void requestStoragePermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (!Environment.isExternalStorageManager()) { Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION); Uri uri = Uri.fromParts("package", getPackageName(), null); intent.setData(uri); startActivityForResult(intent, 100); } else { scanAllStorageVolumes(); } } else { if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 200); } else { scanAllStorageVolumes(); } } } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == 100) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && Environment.isExternalStorageManager()) { scanAllStorageVolumes(); } else { Log.e("##PERMISSION", "用户未授予全存储访问权限"); } } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == 200 && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { scanAllStorageVolumes(); } else { Log.e("##PERMISSION", "用户未授予读取存储权限"); } } private void scanAllStorageVolumes() { StorageManager storageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE); List<StorageVolume> storageVolumes = storageManager.getStorageVolumes(); for (StorageVolume volume : storageVolumes) { boolean isRemovable = volume.isRemovable(); Uri volumeUri = volume.getDirectory(); File volumeDir = new File(volumeUri.getPath()); Log.e("##STORAGE", (isRemovable ? "可移动SD卡" : "内部共享存储") + "根目录:" + volumeDir.getAbsolutePath()); logFilesRecursively(volumeDir); } logFilesInPodcasts(); } private void logFilesRecursively(File directory) { if (directory == null || !directory.exists() || !directory.isDirectory()) { return; } File[] files = directory.listFiles(); if (files == null) { Log.e("##FILE_INFO", "无法访问目录:" + directory.getAbsolutePath()); return; } for (File file : files) { Log.e("##FILE_INFO", (file.isDirectory() ? "[目录]" : "[文件]") + " 名称:" + file.getName() + ",路径:" + file.getAbsolutePath()); if (file.isDirectory()) { logFilesRecursively(file); } } } private void logFilesInPodcasts() { File podcastsFolder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PODCASTS); Log.e("##PODCASTS", "Podcasts目录:" + podcastsFolder.getAbsolutePath()); logFilesRecursively(podcastsFolder); } }
注意事项
MANAGE_EXTERNAL_STORAGE权限会被Google Play审核严格限制,仅文件管理器、备份类等符合政策的应用可申请;- 若仅访问系统公共目录(如Download、Podcasts),推荐使用
Environment.getExternalStoragePublicDirectory()或MediaStore API,无需全存储权限。
内容的提问来源于stack exchange,提问作者S. M. Asif
相关产品推荐
相关产品推荐

