有没有一种通用的方法来找到外置SD卡的位置?
请不要与外部存储混淆。
Environment.getExternalStorageState()返回内部SD挂载点的路径,例如/mnt/sdcard。但问题是关于外部SD的。我如何获得类似/mnt/sdcard/external_sd的路径(它可能因设备而异)?
我想我将以根据文件系统名称过滤mount命令的输出作为结束。但我不确定这种方式是否足够稳健。
有没有一种通用的方法来找到外置SD卡的位置?
请不要与外部存储混淆。
Environment.getExternalStorageState()返回内部SD挂载点的路径,例如/mnt/sdcard。但问题是关于外部SD的。我如何获得类似/mnt/sdcard/external_sd的路径(它可能因设备而异)?
我想我将以根据文件系统名称过滤mount命令的输出作为结束。但我不确定这种方式是否足够稳健。
当前回答
它适用于所有外部设备,但确保只获得外部设备文件夹名,然后你需要从给定的位置使用文件类获取文件。
public static List<String> getExternalMounts() {
final List<String> out = new ArrayList<>();
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;
}
调用:
List<String> list=getExternalMounts();
if(list.size()>0)
{
String[] arr=list.get(0).split("/");
int size=0;
if(arr!=null && arr.length>0) {
size= arr.length - 1;
}
File parentDir=new File("/storage/"+arr[size]);
if(parentDir.listFiles()!=null){
File parent[] = parentDir.listFiles();
for (int i = 0; i < parent.length; i++) {
// get file path as parent[i].getAbsolutePath());
}
}
}
访问外部存储
为了读写外部存储上的文件,你的应用程序必须获得READ_EXTERNAL_STORAGE或WRITE_EXTERNAL_STORAGE系统权限。例如:
<manifest ...>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
...
</manifest>
其他回答
像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卡还是内部不可移动存储),您可以使用以下代码:
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驱动器。
在任何情况下,请注意使用硬编码路径总是一种糟糕的方法(特别是当每个制造商都可以随心所欲地更改它时)。
由于上面我最初的回答,扫描vold在各个制造商中不再可行。
我发明了一种更可靠更直接的方法。
File mnt = new File("/storage");
if (!mnt.exists())
mnt = new File("/mnt");
File[] roots = mnt.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.isDirectory() && pathname.exists()
&& pathname.canWrite() && !pathname.isHidden()
&& !isSymlink(pathname);
}
});
根目录将包含系统上所有可写的根目录,包括任何usb连接的usb设备。
注意:canWrite方法需要android.permission。WRITE_EXTERNAL_STORAGE许可。
如果你看一下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");
我有一个使用ListPreference的应用程序,其中要求用户选择他们想要保存东西的位置。
在那个应用程序中,我扫描了/proc/mounts和/system/etc/vold。Fstab用于sdcard挂载点。我将每个文件的挂载点存储到两个单独的arraylist中。
然后,我将一个列表与另一个列表进行比较,并丢弃了不在两个列表中的项目。这给了我一个到每个sdcard的根路径列表。
从那里,我用File.exists()、File.isDirectory()和File.canWrite()测试了这些路径。如果这些测试中的任何一个为假,我将从列表中丢弃该路径。
无论列表中剩下什么,我都转换为String[]数组,以便ListPreference值属性可以使用它。
您可以在这里查看代码