在Android中获取外部SD卡的位置

11

我有一个应用程序,其中预先存储了大量数据在SD卡中。

我们支持所有平板电脑 ICS 及以后版本。我无法找到一种在所有设备上正确访问SD卡位置的方法。

我查看了 SO 上提供的各种解决方案,但它们似乎不适用于所有情况。正在寻找通用解决方案。

即使有人可以告诉我SD卡的所有可能挂载点也可以。

如果可以缩小解决方案的范围,我仅针对 Android 平板电脑。


2
请查看“使用外部存储”主题,以了解有关数据存储的更多信息:http://developer.android.com/guide/topics/data/data-storage.html。 - Raghunandan
我不建议使用链接中的解决方案。你可以尝试一下,但我建议你遵循文档。 - Raghunandan
@Raghunandan 我来试着回答一下。在文档中,他们要求使用getExternalStorageDirectory(),但我没有很好的体验。它可以在Micromax平板电脑上运行,但不能在beyond和ambrane(基于中国平板电脑)上运行,也不能在三星Galaxy Tab上运行。 - phoenixwizard
我也曾经有同样的困惑。当我看到 CommonsWare 的一个答案时,我解决了这个问题。该解决方案不被推荐,并且在某些设备上可能无法正常工作。我没有在所有设备上测试过它。如果你想使用它,请放心尝试。 - Raghunandan
糟糕!标题中已经有了 :-) 在我看来,我们无法绝对保证解决方案。我建议遵循@Raghunandan的建议。 - andy256
显示剩余5条评论
4个回答

15
很遗憾,这是Android设备高度分散的常见问题。 Environment.getExternalStorageDirectory() 是指设备制造商视为“外部存储器”的任何内容。在某些设备上,它是可移动介质,如SD卡。在某些设备上,它是设备内闪存的一部分。(http://developer.android.com/reference/android/os/Environment.html#getExternalStorageDirectory())在这里,“外部存储器”表示“挂载在主机上时通过USB大容量存储模式访问的驱动器”。如果设备制造商选择将外部存储器置于板载闪存中并配备了SD卡,则需要与该制造商联系以确定是否可以使用SD卡。对于大多数符合Android规范(来自谷歌兼容性列表中已知的设备),Environment.getExternalStorageDirectory() 应该有效。或者您可以编写一个自定义存储类,查看安装点并提供正确的SD卡路径。这是我实现的一些东西,到目前为止效果不错。
public class StorageOptions {
    private static ArrayList<String> mMounts = new ArrayList<String>();
    private static ArrayList<String> mVold = new ArrayList<String>();

    public static String[] labels;
    public static String[] paths;
    public static int count = 0;
    private static final String TAG = StorageOptions.class.getSimpleName();

    public static void determineStorageOptions() {
        readMountsFile();

        readVoldFile();

        compareMountsWithVold();

        testAndCleanMountsList();

        setProperties();
    }

    private static void readMountsFile() {
        /*
         * Scan the /proc/mounts file and look for lines like this:
         * /dev/block/vold/179:1 /mnt/sdcard vfat
         * rw,dirsync,nosuid,nodev,noexec,
         * relatime,uid=1000,gid=1015,fmask=0602,dmask
         * =0602,allow_utime=0020,codepage
         * =cp437,iocharset=iso8859-1,shortname=mixed,utf8,errors=remount-ro 0 0
         * 
         * When one is found, split it into its elements and then pull out the
         * path to the that mount point and add it to the arraylist
         */

        // some mount files don't list the default
        // path first, so we add it here to
        // ensure that it is first in our list
        mMounts.add("/mnt/sdcard");

        try {
            Scanner scanner = new Scanner(new File("/proc/mounts"));
            while (scanner.hasNext()) {
                String line = scanner.nextLine();
                if (line.startsWith("/dev/block/vold/")) {
                    String[] lineElements = line.split(" ");
                    String element = lineElements[1];

                    // don't add the default mount path
                    // it's already in the list.
                    if (!element.equals("/mnt/sdcard"))
                        mMounts.add(element);
                }
            }
        } catch (Exception e) {
            // Auto-generated catch block

            e.printStackTrace();
        }
    }

    private static void readVoldFile() {
        /*
         * Scan the /system/etc/vold.fstab file and look for lines like this:
         * dev_mount sdcard /mnt/sdcard 1
         * /devices/platform/s3c-sdhci.0/mmc_host/mmc0
         * 
         * When one is found, split it into its elements and then pull out the
         * path to the that mount point and add it to the arraylist
         */

        // some devices are missing the vold file entirely
        // so we add a path here to make sure the list always
        // includes the path to the first sdcard, whether real
        // or emulated.
        mVold.add("/mnt/sdcard");

        try {
            Scanner scanner = new Scanner(new File("/system/etc/vold.fstab"));
            while (scanner.hasNext()) {
                String line = scanner.nextLine();
                if (line.startsWith("dev_mount")) {
                    String[] lineElements = line.split(" ");
                    String element = lineElements[2];

                    if (element.contains(":"))
                        element = element.substring(0, element.indexOf(":"));

                    // don't add the default vold path
                    // it's already in the list.
                    if (!element.equals("/mnt/sdcard"))
                        mVold.add(element);
                }
            }
        } catch (Exception e) {
            // Auto-generated catch block

            e.printStackTrace();
        }
    }

    private static void compareMountsWithVold() {
        /*
         * Sometimes the two lists of mount points will be different. We only
         * want those mount points that are in both list.
         * 
         * Compare the two lists together and remove items that are not in both
         * lists.
         */

        for (int i = 0; i < mMounts.size(); i++) {
            String mount = mMounts.get(i);
            if (!mVold.contains(mount))
                mMounts.remove(i--);
        }

        // don't need this anymore, clear the vold list to reduce memory
        // use and to prepare it for the next time it's needed.
        mVold.clear();
    }

    private static void testAndCleanMountsList() {
        /*
         * Now that we have a cleaned list of mount paths Test each one to make
         * sure it's a valid and available path. If it is not, remove it from
         * the list.
         */

        for (int i = 0; i < mMounts.size(); i++) {
            String mount = mMounts.get(i);
            File root = new File(mount);
            if (!root.exists() || !root.isDirectory() || !root.canWrite())
                mMounts.remove(i--);
        }
    }

    @SuppressWarnings("unchecked")
    private static void setProperties() {
        /*
         * At this point all the paths in the list should be valid. Build the
         * public properties.
         */
        Constants.mMounts = new ArrayList<String>();
        ArrayList<String> mLabels = new ArrayList<String>();

        int j = 0;
        if (mMounts.size() > 0) {
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD)
                mLabels.add("Auto");
            else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
                if (Environment.isExternalStorageRemovable()) {
                    mLabels.add("External SD Card 1");
                    j = 1;
                } else
                    mLabels.add("Internal Storage");
            } else {
                if (!Environment.isExternalStorageRemovable()
                        || Environment.isExternalStorageEmulated())
                    mLabels.add("Internal Storage");
                else {
                    mLabels.add("External SD Card 1");
                    j = 1;
                }
            }

            if (mMounts.size() > 1) {
                for (int i = 1; i < mMounts.size(); i++) {
                    mLabels.add("External SD Card " + (i + j));
                }
            }
        }

        labels = new String[mLabels.size()];
        mLabels.toArray(labels);

        paths = new String[mMounts.size()];
        mMounts.toArray(paths);
        Constants.mMounts = (ArrayList<String>) mMounts.clone();
        Constants.mLabels = (ArrayList<String>) mLabels.clone();
        count = Math.min(labels.length, paths.length);

        // don't need this anymore, clear the mounts list to reduce memory
        // use and to prepare it for the next time it's needed.
        mMounts.clear();

    }
}

我从一个类似的SO问题中找到了这个,不幸的是,我没有链接,但可能在Sony的Android开发者网站上有(也不幸的是那里也没有链接)。Wagic - 一个C ++游戏引擎库实现了相同的功能,他们的代码在这里:http://wagic.googlecode.com/svn-history/r4300/trunk/projects/mtg/Android/src/net/wagic/utils/StorageOptions.java,所以你可以查看实现。我希望来自Google的人可以回答这个问题,并提供一种从所有Android设备中读取SD卡挂载点的单一方式。


只有一个疑问。从哪里获取 Constants.mMounts? - phoenixwizard
哦,抱歉那是我特定实现的类——一个包含所有应用常量的类,对你来说不是必须的,你可以实现自己的。 - Vrashabh Irde
1
看起来它没有考虑到带有/mnt/extsd的选项卡 :( - phoenixwizard
1
它实际上应该返回所有可能卡的哈希值。如果没有,那么不幸的是,您必须识别它并硬编码,如果您想制作一个真正全球化的Android应用程序,可以在使用此extsd符号的中国平板电脑等平板电脑上使用。 - Vrashabh Irde
确实,这无法找到像已挂载的SD卡这样的次要挂载点。 - lostintranslation

1

这并不像你想的那么简单, String f = Environment.getExternalStorageDirectory().getAbsolutePath(); Log.v("TAG",f);//打印sdcard路径

它返回的是设备存储路径,而不是外部SD卡路径。


-1
如果您想获取SD卡的路径,请使用以下代码: String baseDir = Environment.getExternalStorageDirectory().getAbsolutePath(); 然后使用以下代码获取特定文件夹/文件的路径: String path = baseDir + "/your folder(s)/" + fileName;

3
并非所有设备都适用此项。 - phoenixwizard

-6

很简单。

String f = Environment.getExternalStorageDirectory().getAbsolutePath();
Log.v("TAG",f);//to print the path of sdcard

如果你想访问一个文件,那么。

String f =  Environment.getExternalStorageDirectory()+"/file.ext";
Log.v("TAG",f);//to print the path of file in Logcat.

1
"for ALL devices"对于所有设备 - Tala
它只是不返回外部SD卡的路径。 - FD_
该回答未解决问题的主要问题,即“获取所有Android设备的外部SD卡路径”。 - FeleMed

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接