如何获得Android 4.0+的外部SD卡路径?

Rom*_*'ai 91 android android-sdcard

三星Galaxy S3有一个extrenal SD卡插槽,安装到/mnt/extSdCard.

我的问题是:如何通过类似的方式获得这条路径Environment.getExternalStorageDirectory()?这将返回mnt/sdcard,我找不到外部SD卡的API.(或某些平板电脑上的可移动USB存储器)

谢谢!

Gna*_*nic 56

我对这里找到的解决方案有所不同

public static HashSet<String> getExternalMounts() {
    final HashSet<String> out = new HashSet<String>();
    String reg = "(?i).*vold.*(vfat|ntfs|exfat|fat32|ext3|ext4).*rw.*";
    String s = "";
    try {
        final Process process = new ProcessBuilder().command("mount")
                .redirectErrorStream(true).start();
        process.waitFor();
        final InputStream is = process.getInputStream();
        final byte[] buffer = new byte[1024];
        while (is.read(buffer) != -1) {
            s = s + new String(buffer);
        }
        is.close();
    } catch (final Exception e) {
        e.printStackTrace();
    }

    // parse output
    final String[] lines = s.split("\n");
    for (String line : lines) {
        if (!line.toLowerCase(Locale.US).contains("asec")) {
            if (line.matches(reg)) {
                String[] parts = line.split(" ");
                for (String part : parts) {
                    if (part.startsWith("/"))
                        if (!part.toLowerCase(Locale.US).contains("vold"))
                            out.add(part);
                }
            }
        }
    }
    return out;
}
Run Code Online (Sandbox Code Playgroud)

原始方法经过测试和使用

  • 华为X3(现货)
  • Galaxy S2(股票)
  • Galaxy S3(股票)

我不确定它们在测试时会使用哪个Android版本.

我已经测试了我的修改版本

  • Moto Xoom 4.1.2(股票)
  • 使用otg电缆的Galaxy Nexus(cyanogenmod 10)
  • HTC Incredible(cyanogenmod 7.2)这个内部和外部都返回了.这个设备有点奇怪,因为它的内部很大程度上未使用,因为getExternalStorage()返回一个sdcard的路径.

以及一些使用SD卡作为主存储的单个存储设备

  • HTC G1(cyanogenmod 6.1)
  • HTC G1(股票)
  • HTC Vision/G2(现货)

除了令人难以置信的所有这些设备只返回其可移动存储.我可能会做一些额外的检查,但这至少比我迄今为止找到的任何解决方案都要好一些.

  • 我认为如果原始文件夹包含像/ mnt/SdCard这样的大写字母,解决方案会给出无效的文件夹名称 (3认同)
  • @KhawarRaza,这种方法从kitkat开始停止工作.使用Dmitriy Lozenko的方法,如果你支持pre-icics设备,这将作为后备. (2认同)
  • 这是在Galaxy Note 3上为我返回/ mnt而不是4.0+/storage (2认同)

小智 53

我找到了更可靠的方法来获取系统中所有SD-CARD的路径.这适用于所有Android版本并返回所有存储的路径(包括模拟).

在我的所有设备上正常工作.

PS:基于Environment类的源代码.

private static final Pattern DIR_SEPORATOR = Pattern.compile("/");

/**
 * Raturns all available SD-Cards in the system (include emulated)
 *
 * Warning: Hack! Based on Android source code of version 4.3 (API 18)
 * Because there is no standart way to get it.
 * TODO: Test on future Android versions 4.4+
 *
 * @return paths to all available SD-Cards in the system (include emulated)
 */
public static String[] getStorageDirectories()
{
    // Final set of paths
    final Set<String> rv = new HashSet<String>();
    // Primary physical SD-CARD (not emulated)
    final String rawExternalStorage = System.getenv("EXTERNAL_STORAGE");
    // All Secondary SD-CARDs (all exclude primary) separated by ":"
    final String rawSecondaryStoragesStr = System.getenv("SECONDARY_STORAGE");
    // Primary emulated SD-CARD
    final String rawEmulatedStorageTarget = System.getenv("EMULATED_STORAGE_TARGET");
    if(TextUtils.isEmpty(rawEmulatedStorageTarget))
    {
        // Device has physical external storage; use plain paths.
        if(TextUtils.isEmpty(rawExternalStorage))
        {
            // EXTERNAL_STORAGE undefined; falling back to default.
            rv.add("/storage/sdcard0");
        }
        else
        {
            rv.add(rawExternalStorage);
        }
    }
    else
    {
        // Device has emulated storage; external storage paths should have
        // userId burned into them.
        final String rawUserId;
        if(Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1)
        {
            rawUserId = "";
        }
        else
        {
            final String path = Environment.getExternalStorageDirectory().getAbsolutePath();
            final String[] folders = DIR_SEPORATOR.split(path);
            final String lastFolder = folders[folders.length - 1];
            boolean isDigit = false;
            try
            {
                Integer.valueOf(lastFolder);
                isDigit = true;
            }
            catch(NumberFormatException ignored)
            {
            }
            rawUserId = isDigit ? lastFolder : "";
        }
        // /storage/emulated/0[1,2,...]
        if(TextUtils.isEmpty(rawUserId))
        {
            rv.add(rawEmulatedStorageTarget);
        }
        else
        {
            rv.add(rawEmulatedStorageTarget + File.separator + rawUserId);
        }
    }
    // Add all secondary storages
    if(!TextUtils.isEmpty(rawSecondaryStoragesStr))
    {
        // All Secondary SD-CARDs splited into array
        final String[] rawSecondaryStorages = rawSecondaryStoragesStr.split(File.pathSeparator);
        Collections.addAll(rv, rawSecondaryStorages);
    }
    return rv.toArray(new String[rv.size()]);
}
Run Code Online (Sandbox Code Playgroud)

  • DIR_SEPORATOR假设是什么? (5认同)
  • 适用于棒棒糖;不适用于棉花糖(带有 6.0.1 的 Galaxy Tab A),其中 `System.getenv("SECONDARY_STORAGE")` 返回 `/storage/sdcard1`。该位置不存在,但实际位置是`/storage/42CE-FD0A`,其中42CE-FD0A 是格式化分区的卷序列号。 (2认同)

Fab*_*ook 32

我想使用你需要使用的外部SD卡:

new File("/mnt/external_sd/")
Run Code Online (Sandbox Code Playgroud)

要么

new File("/mnt/extSdCard/")
Run Code Online (Sandbox Code Playgroud)

在你的情况下......

取代 Environment.getExternalStorageDirectory()

适合我.您应该首先检查目录mnt中的内容,然后从那里开始工作.


您应该使用某种类型的选择方法来选择使用哪个SD卡:

File storageDir = new File("/mnt/");
if(storageDir.isDirectory()){
    String[] dirList = storageDir.list();
    //TODO some type of selecton method?
}
Run Code Online (Sandbox Code Playgroud)

  • 不幸的是,外部SD卡可能与/ mnt位于不同的文件夹中.例如在三星GT-I9305上它是/ storage/extSdCard而内置SD卡有路径/存储/ sdcard0 (10认同)
  • 那么你将获得sdcard位置,然后获取其父目录. (2认同)

Pao*_*lli 15

要检索所有外部存储(无论是SD卡还是内部不可移动存储),您可以使用以下代码:

final String state = Environment.getExternalStorageState();

if ( Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state) ) {  // we can read the External Storage...           
    //Retrieve the primary External Storage:
    final File primaryExternalStorage = Environment.getExternalStorageDirectory();

    //Retrieve the External Storages root directory:
    final String externalStorageRootDir;
    if ( (externalStorageRootDir = primaryExternalStorage.getParent()) == null ) {  // no parent...
        Log.d(TAG, "External Storage: " + primaryExternalStorage + "\n");
    }
    else {
        final File externalStorageRoot = new File( externalStorageRootDir );
        final File[] files = externalStorageRoot.listFiles();

        for ( final File file : files ) {
            if ( file.isDirectory() && file.canRead() && (file.listFiles().length > 0) ) {  // it is a real directory (not a USB drive)...
                Log.d(TAG, "External Storage: " + file.getAbsolutePath() + "\n");
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用System.getenv("EXTERNAL_STORAGE")来检索主外部存储目录(例如"/ storage/sdcard0")和System.getenv("SECONDARY_STORAGE")以查找所有辅助目录的列表(例如"/storage/extSdCard:/ storage/UsbDriveA:/ storage/UsbDriveB").请记住,同样在这种情况下,您可能希望过滤辅助目录列表以排除USB驱动器.

在任何情况下,请注意使用硬编码路径始终是一种不好的方法(特别是当每个制造商可能会高兴地改变它时).


Hen*_*aWD 14

我正在使用Dmitriy Lozenko的解决方案,直到我检查了Asus Zenfone2,Marshmallow 6.0.1并且该解决方案无效.获取EMULATED_STORAGE_TARGET时,解决方案失败,特别是对于microSD路径,即:/ storage/F99C-10F4 /.我编辑了代码以直接从模拟的应用程序路径获取模拟的根路径,context.getExternalFilesDirs(null);并添加更多已知的特定于电话模型的物理路径.

为了让我们的生活更轻松,我在这里建了一个图书馆.您可以通过gradle,maven,sbt和leiningen构建系统使用它.

如果你喜欢老式的方式,你也可以直接从这里复制粘贴文件,但是如果没有手动检查,你将不知道将来是否有更新.

如果您有任何问题或建议,请告诉我


Jef*_*key 13

好消息!在KitKat中,现在有一个用于与这些辅助共享存储设备交互的公共API.

新的Context.getExternalFilesDirs()Context.getExternalCacheDirs()方法可以返回多个路径,包括主设备和辅助设备.然后,您可以迭代它们并检查Environment.getStorageState()File.getFreeSpace()确定存储文件的最佳位置.这些方法也可以ContextCompat在support-v4库中找到.

另请注意,如果您只对使用返回的目录感兴趣,则Context不再需要READ_WRITE_EXTERNAL_STORAGE权限.展望未来,您将始终拥有对这些目录的读/写访问权限,而无需其他权限.

应用还可以通过终止其权限请求继续处理旧设备,如下所示:

<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="18" />
Run Code Online (Sandbox Code Playgroud)


val*_*nta 9

我做了以下操作来访问所有外部SD卡.

附:

File primaryExtSd=Environment.getExternalStorageDirectory();
Run Code Online (Sandbox Code Playgroud)

你得到主要外部SD的路径然后:

File parentDir=new File(primaryExtSd.getParent());
Run Code Online (Sandbox Code Playgroud)

您获得主外部存储的父目录,它也是所有外部sd的父目录.现在,您可以列出所有存储并选择所需的存储.

希望它有用.


and*_*per 9

以下是我获取SD卡路径列表(不包括主外部存储)的方法:

  /**
   * returns a list of all available sd cards paths, or null if not found.
   * 
   * @param includePrimaryExternalStorage set to true if you wish to also include the path of the primary external storage
   */
  @TargetApi(Build.VERSION_CODES.HONEYCOMB)
  public static List<String> getSdCardPaths(final Context context,final boolean includePrimaryExternalStorage)
    {
    final File[] externalCacheDirs=ContextCompat.getExternalCacheDirs(context);
    if(externalCacheDirs==null||externalCacheDirs.length==0)
      return null;
    if(externalCacheDirs.length==1)
      {
      if(externalCacheDirs[0]==null)
        return null;
      final String storageState=EnvironmentCompat.getStorageState(externalCacheDirs[0]);
      if(!Environment.MEDIA_MOUNTED.equals(storageState))
        return null;
      if(!includePrimaryExternalStorage&&VERSION.SDK_INT>=VERSION_CODES.HONEYCOMB&&Environment.isExternalStorageEmulated())
        return null;
      }
    final List<String> result=new ArrayList<>();
    if(includePrimaryExternalStorage||externalCacheDirs.length==1)
      result.add(getRootOfInnerSdCardFolder(externalCacheDirs[0]));
    for(int i=1;i<externalCacheDirs.length;++i)
      {
      final File file=externalCacheDirs[i];
      if(file==null)
        continue;
      final String storageState=EnvironmentCompat.getStorageState(file);
      if(Environment.MEDIA_MOUNTED.equals(storageState))
        result.add(getRootOfInnerSdCardFolder(externalCacheDirs[i]));
      }
    if(result.isEmpty())
      return null;
    return result;
    }

  /** Given any file/folder inside an sd card, this will return the path of the sd card */
  private static String getRootOfInnerSdCardFolder(File file)
    {
    if(file==null)
      return null;
    final long totalSpace=file.getTotalSpace();
    while(true)
      {
      final File parentFile=file.getParentFile();
      if(parentFile==null||parentFile.getTotalSpace()!=totalSpace)
        return file.getAbsolutePath();
      file=parentFile;
      }
    }
Run Code Online (Sandbox Code Playgroud)


rml*_*rml 5

感谢你们提供的线索,特别是@SmartLemon,我得到了解决方案.如果其他人需要它,我把我的最终解决方案放在这里(找到第一个列出的外部SD卡):

public File getExternalSDCardDirectory()
{
    File innerDir = Environment.getExternalStorageDirectory();
    File rootDir = innerDir.getParentFile();
    File firstExtSdCard = innerDir ;
    File[] files = rootDir.listFiles();
    for (File file : files) {
        if (file.compareTo(innerDir) != 0) {
            firstExtSdCard = file;
            break;
        }
    }
    //Log.i("2", firstExtSdCard.getAbsolutePath().toString());
    return firstExtSdCard;
}
Run Code Online (Sandbox Code Playgroud)

如果没有外部SD卡,则返回板载存储.如果sdcard不存在我会使用它,你可能需要更改它.