有没有一种通用的方法来找到外置SD卡的位置?
请不要与外部存储混淆。
Environment.getExternalStorageState()返回内部SD挂载点的路径,例如/mnt/sdcard。但问题是关于外部SD的。我如何获得类似/mnt/sdcard/external_sd的路径(它可能因设备而异)?
我想我将以根据文件系统名称过滤mount命令的输出作为结束。但我不确定这种方式是否足够稳健。
有没有一种通用的方法来找到外置SD卡的位置?
请不要与外部存储混淆。
Environment.getExternalStorageState()返回内部SD挂载点的路径,例如/mnt/sdcard。但问题是关于外部SD的。我如何获得类似/mnt/sdcard/external_sd的路径(它可能因设备而异)?
我想我将以根据文件系统名称过滤mount命令的输出作为结束。但我不确定这种方式是否足够稳健。
当前回答
我有一个使用ListPreference的应用程序,其中要求用户选择他们想要保存东西的位置。
在那个应用程序中,我扫描了/proc/mounts和/system/etc/vold。Fstab用于sdcard挂载点。我将每个文件的挂载点存储到两个单独的arraylist中。
然后,我将一个列表与另一个列表进行比较,并丢弃了不在两个列表中的项目。这给了我一个到每个sdcard的根路径列表。
从那里,我用File.exists()、File.isDirectory()和File.canWrite()测试了这些路径。如果这些测试中的任何一个为假,我将从列表中丢弃该路径。
无论列表中剩下什么,我都转换为String[]数组,以便ListPreference值属性可以使用它。
您可以在这里查看代码
其他回答
我已经创建了一个utils方法来检查SD卡在设备上是否可用,并获得设备上的SD卡路径(如果可用)。
你可以复制2方法波纹到你的项目的类,你需要。这是所有。
public String isRemovableSDCardAvailable() {
final String FLAG = "mnt";
final String SECONDARY_STORAGE = System.getenv("SECONDARY_STORAGE");
final String EXTERNAL_STORAGE_DOCOMO = System.getenv("EXTERNAL_STORAGE_DOCOMO");
final String EXTERNAL_SDCARD_STORAGE = System.getenv("EXTERNAL_SDCARD_STORAGE");
final String EXTERNAL_SD_STORAGE = System.getenv("EXTERNAL_SD_STORAGE");
final String EXTERNAL_STORAGE = System.getenv("EXTERNAL_STORAGE");
Map<Integer, String> listEnvironmentVariableStoreSDCardRootDirectory = new HashMap<Integer, String>();
listEnvironmentVariableStoreSDCardRootDirectory.put(0, SECONDARY_STORAGE);
listEnvironmentVariableStoreSDCardRootDirectory.put(1, EXTERNAL_STORAGE_DOCOMO);
listEnvironmentVariableStoreSDCardRootDirectory.put(2, EXTERNAL_SDCARD_STORAGE);
listEnvironmentVariableStoreSDCardRootDirectory.put(3, EXTERNAL_SD_STORAGE);
listEnvironmentVariableStoreSDCardRootDirectory.put(4, EXTERNAL_STORAGE);
File externalStorageList[] = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
externalStorageList = getContext().getExternalFilesDirs(null);
}
String directory = null;
int size = listEnvironmentVariableStoreSDCardRootDirectory.size();
for (int i = 0; i < size; i++) {
if (externalStorageList != null && externalStorageList.length > 1 && externalStorageList[1] != null)
directory = externalStorageList[1].getAbsolutePath();
else
directory = listEnvironmentVariableStoreSDCardRootDirectory.get(i);
directory = canCreateFile(directory);
if (directory != null && directory.length() != 0) {
if (i == size - 1) {
if (directory.contains(FLAG)) {
Log.e(getClass().getSimpleName(), "SD Card's directory: " + directory);
return directory;
} else {
return null;
}
}
Log.e(getClass().getSimpleName(), "SD Card's directory: " + directory);
return directory;
}
}
return null;
}
/**
* Check if can create file on given directory. Use this enclose with method
* {@link BeginScreenFragement#isRemovableSDCardAvailable()} to check sd
* card is available on device or not.
*
* @param directory
* @return
*/
public String canCreateFile(String directory) {
final String FILE_DIR = directory + File.separator + "hoang.txt";
File tempFlie = null;
try {
tempFlie = new File(FILE_DIR);
FileOutputStream fos = new FileOutputStream(tempFlie);
fos.write(new byte[1024]);
fos.flush();
fos.close();
Log.e(getClass().getSimpleName(), "Can write file on this directory: " + FILE_DIR);
} catch (Exception e) {
Log.e(getClass().getSimpleName(), "Write file error: " + e.getMessage());
return null;
} finally {
if (tempFlie != null && tempFlie.exists() && tempFlie.isFile()) {
// tempFlie.delete();
tempFlie = null;
}
}
return directory;
}
如果你看一下Android .os. environment的源代码,你会发现Android非常依赖环境变量作为路径。您可以使用“SECONDARY_STORAGE”环境变量来查找到可移动sd卡的路径。
/**
* Get a file using an environmental variable.
*
* @param variableName
* The Environment variable name.
* @param paths
* Any paths to the file if the Environment variable was not found.
* @return the File or {@code null} if the File could not be located.
*/
private static File getDirectory(String variableName, String... paths) {
String path = System.getenv(variableName);
if (!TextUtils.isEmpty(path)) {
if (path.contains(":")) {
for (String _path : path.split(":")) {
File file = new File(_path);
if (file.exists()) {
return file;
}
}
} else {
File file = new File(path);
if (file.exists()) {
return file;
}
}
}
if (paths != null && paths.length > 0) {
for (String _path : paths) {
File file = new File(_path);
if (file.exists()) {
return file;
}
}
}
return null;
}
使用示例:
public static final File REMOVABLE_STORAGE = getDirectory("SECONDARY_STORAGE");
为了检索所有外部存储(无论是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");
}
}
}
}
或者,您可以使用System.getenv("EXTERNAL_STORAGE")来检索主要的外部存储目录(例如:"/storage/sdcard0")和System.getenv("SECONDARY_STORAGE")来检索所有从目录的列表(例如:“/存储/ extSdCard: /存储/ UsbDriveA: /存储/ UsbDriveB”)。记住,同样在这种情况下,您可能想要过滤从目录列表以排除USB驱动器。
在任何情况下,请注意使用硬编码路径总是一种糟糕的方法(特别是当每个制造商都可以随心所欲地更改它时)。
有没有一种通用的方法来找到外置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文档,内部和外部存储术语与我们想象的大不相同。
你可以尝试使用支持库函数ContextCompat.getExternalFilesDirs():
final File[] appsDir=ContextCompat.getExternalFilesDirs(getActivity(),null);
final ArrayList<File> extRootPaths=new ArrayList<>();
for(final File file : appsDir)
extRootPaths.add(file.getParentFile().getParentFile().getParentFile().getParentFile());
第一个是主要的外部存储,其余的应该是真正的sd卡路径。
使用多个“.getParentFile()”的原因是要转到另一个文件夹,因为原始路径是
.../Android/data/YOUR_APP_PACKAGE_NAME/files/
编辑:这是一个更全面的方法,我已经创建,以获得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||!parentFile.canRead())
return file.getAbsolutePath();
file=parentFile;
}
}
编辑:这里有更好的解决方案:
https://stackoverflow.com/a/27197248/878126