You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
    }
}

注意事项

  1. MANAGE_EXTERNAL_STORAGE权限会被Google Play审核严格限制,仅文件管理器、备份类等符合政策的应用可申请;
  2. 若仅访问系统公共目录(如Download、Podcasts),推荐使用Environment.getExternalStoragePublicDirectory()或MediaStore API,无需全存储权限。

内容的提问来源于stack exchange,提问作者S. M. Asif

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 00:44:57