有没有一种通用的方法来找到外置SD卡的位置?

请不要与外部存储混淆。

Environment.getExternalStorageState()返回内部SD挂载点的路径,例如/mnt/sdcard。但问题是关于外部SD的。我如何获得类似/mnt/sdcard/external_sd的路径(它可能因设备而异)?

我想我将以根据文件系统名称过滤mount命令的输出作为结束。但我不确定这种方式是否足够稳健。


当前回答

我不知道为什么,但我需要在使用它之前在公共存储目录中创建的文件上调用. createnewfile()。在框架中,该方法的注释说它没有用。这是一个样本……

String myPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PODCASTS) + File. txt文件。separator +“我的目录”; final File myDir = new File(myypath); 尝试{ myDir.mkdirs (); } catch(异常ex) { 吐司。makeText(this, "error: " + ex.getMessage(), Toast.LENGTH_LONG).show(); }

        String fname = "whatever";
        File newFile = new File(myDir, fname);

        Log.i(TAG, "File exists --> " + newFile.exists()) //will be false  
    try {
            if (newFile.createNewFile()) {

                 //continue 

              } else {

                Log.e(TAG, "error creating file");

            }

        } catch (Exception e) {
            Log.e(TAG, e.toString());
        }

其他回答

我不知道为什么,但我需要在使用它之前在公共存储目录中创建的文件上调用. createnewfile()。在框架中,该方法的注释说它没有用。这是一个样本……

String myPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PODCASTS) + File. txt文件。separator +“我的目录”; final File myDir = new File(myypath); 尝试{ myDir.mkdirs (); } catch(异常ex) { 吐司。makeText(this, "error: " + ex.getMessage(), Toast.LENGTH_LONG).show(); }

        String fname = "whatever";
        File newFile = new File(myDir, fname);

        Log.i(TAG, "File exists --> " + newFile.exists()) //will be false  
    try {
            if (newFile.createNewFile()) {

                 //continue 

              } else {

                Log.e(TAG, "error creating file");

            }

        } catch (Exception e) {
            Log.e(TAG, e.toString());
        }

这是我用来找到外部卡的方法。使用mount cmd返回然后解析vfat部分。

String s = "";
try {
Process process = new ProcessBuilder().command("mount")
        .redirectErrorStream(true).start();

process.waitFor();

InputStream is = process.getInputStream();
byte[] buffer = new byte[1024];
while (is.read(buffer) != -1) {
    s = s + new String(buffer);
}
is.close();
} catch (Exception e) {
e.printStackTrace();
}

//用行分隔mount列表
String[] lines = s.split("\n");
for(int i=0; i<lines.length; i++) {
//如果行内有挂载路径且为vfat类型,说明可能是内置或者外置sd的挂载点
if(-1 != lines[i].indexOf(path[0]) && -1 != lines[i].indexOf("vfat")) {
    //再用空格分隔
    String[] blocks = lines[i].split("\\s");
    for(int j=0; j<blocks.length; j++) {
        //判断是否是挂载为vfat类型
        if(-1 != blocks[j].indexOf(path[0])) {
            //Test if it is the external sd card.
        }
    }
}
}

像Richard一样,我也使用/proc/mounts文件来获取可用的存储选项列表

public class StorageUtils {

    private static final String TAG = "StorageUtils";

    public static class StorageInfo {

        public final String path;
        public final boolean internal;
        public final boolean readonly;
        public final int display_number;

        StorageInfo(String path, boolean internal, boolean readonly, int display_number) {
            this.path = path;
            this.internal = internal;
            this.readonly = readonly;
            this.display_number = display_number;
        }

        public String getDisplayName() {
            StringBuilder res = new StringBuilder();
            if (internal) {
                res.append("Internal SD card");
            } else if (display_number > 1) {
                res.append("SD card " + display_number);
            } else {
                res.append("SD card");
            }
            if (readonly) {
                res.append(" (Read only)");
            }
            return res.toString();
        }
    }

    public static List<StorageInfo> getStorageList() {

        List<StorageInfo> list = new ArrayList<StorageInfo>();
        String def_path = Environment.getExternalStorageDirectory().getPath();
        boolean def_path_internal = !Environment.isExternalStorageRemovable();
        String def_path_state = Environment.getExternalStorageState();
        boolean def_path_available = def_path_state.equals(Environment.MEDIA_MOUNTED)
                                    || def_path_state.equals(Environment.MEDIA_MOUNTED_READ_ONLY);
        boolean def_path_readonly = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY);
        BufferedReader buf_reader = null;
        try {
            HashSet<String> paths = new HashSet<String>();
            buf_reader = new BufferedReader(new FileReader("/proc/mounts"));
            String line;
            int cur_display_number = 1;
            Log.d(TAG, "/proc/mounts");
            while ((line = buf_reader.readLine()) != null) {
                Log.d(TAG, line);
                if (line.contains("vfat") || line.contains("/mnt")) {
                    StringTokenizer tokens = new StringTokenizer(line, " ");
                    String unused = tokens.nextToken(); //device
                    String mount_point = tokens.nextToken(); //mount point
                    if (paths.contains(mount_point)) {
                        continue;
                    }
                    unused = tokens.nextToken(); //file system
                    List<String> flags = Arrays.asList(tokens.nextToken().split(",")); //flags
                    boolean readonly = flags.contains("ro");

                    if (mount_point.equals(def_path)) {
                        paths.add(def_path);
                        list.add(0, new StorageInfo(def_path, def_path_internal, readonly, -1));
                    } else if (line.contains("/dev/block/vold")) {
                        if (!line.contains("/mnt/secure")
                            && !line.contains("/mnt/asec")
                            && !line.contains("/mnt/obb")
                            && !line.contains("/dev/mapper")
                            && !line.contains("tmpfs")) {
                            paths.add(mount_point);
                            list.add(new StorageInfo(mount_point, false, readonly, cur_display_number++));
                        }
                    }
                }
            }

            if (!paths.contains(def_path) && def_path_available) {
                list.add(0, new StorageInfo(def_path, def_path_internal, def_path_readonly, -1));
            }

        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            if (buf_reader != null) {
                try {
                    buf_reader.close();
                } catch (IOException ex) {}
            }
        }
        return list;
    }    
}

有没有一种通用的方法来找到外置SD卡的位置?

通用方式,如果你指的是官方方式;是的,有一个。

在API级别19中,即在Android版本4.4 Kitkat中,他们在上下文类中添加了File[] getExternalFilesDirs(字符串类型),允许应用程序在micro SD卡中存储数据/文件。

Android 4.4是第一个允许应用程序使用SD卡存储的平台。API级别19之前对SD卡的任何访问都是通过私有的、不受支持的API。

getExternalFilesDirs(String类型)返回所有共享/外部存储设备上特定于应用程序目录的绝对路径。这意味着,它将返回内部和外部内存的路径。通常,第二个返回路径将是microSD卡的存储路径(如果有的话)。

但请注意,

共享存储可能并不总是可用的,因为可移动媒体可以 被用户弹出。媒体状态可以检查使用 getExternalStorageState(文件)。 这些文件没有安全措施。例如,任何 持有WRITE_EXTERNAL_STORAGE的应用程序可以写入这些文件。

根据谷歌/官方Android文档,内部和外部存储术语与我们想象的大不相同。

我有一个使用ListPreference的应用程序,其中要求用户选择他们想要保存东西的位置。

在那个应用程序中,我扫描了/proc/mounts和/system/etc/vold。Fstab用于sdcard挂载点。我将每个文件的挂载点存储到两个单独的arraylist中。

然后,我将一个列表与另一个列表进行比较,并丢弃了不在两个列表中的项目。这给了我一个到每个sdcard的根路径列表。

从那里,我用File.exists()、File.isDirectory()和File.canWrite()测试了这些路径。如果这些测试中的任何一个为假,我将从列表中丢弃该路径。

无论列表中剩下什么,我都转换为String[]数组,以便ListPreference值属性可以使用它。

您可以在这里查看代码