今天做项目(galaxy s4 测试)的时候发现,Environment.getExternalStorageDirectory().getPath();得到的是SDcard路径为内置的SDcard路径。并不是我们外置的cd卡路径。通过查找资料得到以下结果。
发现有两种不一样的方式:
1、读取/proc/mounts,/system/etc/vold.fstab (在s4测试中通过,建议用这种方式)
import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Scanner; import android.os.Environment; import android.util.Log; public class ExternalStorage { public static final String SD_CARD = "sdCard"; public static final String EXTERNAL_SD_CARD = "externalSdCard"; /** * @return True if the external storage is available. False otherwise. */ public static boolean isAvailable() { String state = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { return true; } return false; } public static String getSdCardPath() { return Environment.getExternalStorageDirectory().getPath() + "/"; } /** * @return True if the external storage is writable. False otherwise. */ public static boolean isWritable() { String state = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(state)) { return true; } return false; } /** * @return A map of all storage locations available */ public static Map<String, File> getAllStorageLocations() { Map<String, File> map = new HashMap<String, File>(10); List<String> mMounts = new ArrayList<String>(10); List<String> mVold = new ArrayList<String>(10); mMounts.add("/mnt/sdcard"); mVold.add("/mnt/sdcard"); try { File mountFile = new File("/proc/mounts"); if(mountFile.exists()){ Scanner scanner = new Scanner(mountFile); 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) { e.printStackTrace(); } try { File voldFile = new File("/system/etc/vold.fstab"); if(voldFile.exists()){ Scanner scanner = new Scanner(voldFile); 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(":")); if (!element.equals("/mnt/sdcard")) mVold.add(element); } } } } catch (Exception e) { e.printStackTrace(); } for (int i = 0; i < mMounts.size(); i++) { String mount = mMounts.get(i); if (!mVold.contains(mount)) mMounts.remove(i--); } mVold.clear(); List<String> mountHash = new ArrayList<String>(10); for(String mount : mMounts){ File root = new File(mount); if (root.exists() && root.isDirectory() && root.canWrite()) { File[] list = root.listFiles(); String hash = "["; if(list!=null){ for(File f : list){ hash += f.getName().hashCode()+":"+f.length()+", "; } } hash += "]"; if(!mountHash.contains(hash)){ String key = SD_CARD + "_" + map.size(); if (map.size() == 0) { key = SD_CARD; } else if (map.size() == 1) { key = EXTERNAL_SD_CARD; } mountHash.add(hash); map.put(key, root); } } } mMounts.clear(); if(map.isEmpty()){ map.put(SD_CARD, Environment.getExternalStorageDirectory()); } return map; } }
import java.io.File;
import java.util.ArrayList;
import java.util.Scanner;
import android.os.StatFs;
import android.util.Log;
/**
* 此类设计的用途、实现的思路介绍如下:
*
* 用途:用于在不确定可使用的路径下,通过此类的接口 getStorageCard()返回有效路径;
* 思路:
* 1、读取系统对外卡挂载的路径/proc/mounts,获取挂载的外卡路径并与系统对挂载路径的设置文件
* /system/etc/vold.fstab进行匹配,如果与设置文件的路径一致,则将挂载路径保存在mMounts中,不一致则删除;
* 2、得到挂载路径后同时将本地硬盘的可用路径添加进去;
* 3、遍历这些路径(挂载路径和本地Flash可用路径)下的所有文件,包括子级下的所有文件夹和文件,直到找到与FollowMe匹配的
* 为止;
* 4、在第三步中如果找到匹配的FollowMe,即返回FollowMe的父路径;如果所有的路径下都没有找到FollowMe,那么再
* 在这些路径下找到可用内存空间最大的盘符路径返回;
* 5、全部完成.
*
* 只提供 getStorageCard()接口供外部调用,并返回有效路径.
*/
public class MAObtainAvaliStoragePath {
private static ArrayList<String> mMounts = new ArrayList<String>();//挂载路径和本地Flash可用路径的集合
private static ArrayList<String> mVold = new ArrayList<String>();///system/etc/vold.fstab文件中对所有挂载路径的集合
private boolean flag = false; // 是否找到指定文件目录标志 false未找到 true 找到
private String resultFileDir;// 指定查找目录的父目录
public MAObtainAvaliStoragePath() {}
/**
* 获取有效路径
* @return 返回有效的绝对路径
*/
public String getStorageCard() {
readMountsFile();
readVoldFile();
compareMountsWithVold();
ArrayList<String> paramArrayList = new ArrayList<String>();
String parentPath = new String();
int i = 0;
mMounts.add(0, "/data");
int j = mMounts.size();
while (true) {
if (i >= j) {
mMounts.clear();
mVold.clear();
return null;
}
for (int k = 0; k < j; k++) {
String str = (String) mMounts.get(k);
paramArrayList.add(str);
}
parentPath = getParentPath(paramArrayList);
if (parentPath != null) {
return parentPath;
}
return null;
}
}
/**
* 获取有效的绝对路径
* @param allPath 所有外卡路径 和本地可用的Flash路径
* @return String:返回路径
*/
private String getParentPath(ArrayList<String> allPath) {
String str = null;
if (allPath != null) {
for (int i = 0; i < allPath.size(); i++) {
File foldFile = new File(allPath.get(i));
Log.i("info", "file" + foldFile.toString() + " -->i = " + i);
flag = fileList(foldFile);
if (flag) {
str = resultFileDir;
return str;
} else {
Log.i("info", "此路径下没有发现需要的文件名");
continue;
}
}
//在所有路径下没有找到FollowMe后,判断所有路径下可使用内存的大小并将可使用的最大内存路径返回
if (str == null) {
str = getFreeSize(allPath)+"/FollowMe";
File f = new File(str);
if (!f.exists()) {
f.getParentFile().mkdirs();
}
}
}
return str;
}
/**
* 获取可使用的最大内存空间路径
* @param path 所有外卡路径 和本地可用的Flash路径
* @return String:可使用的最大内存空间的绝对路径
*/
private String getFreeSize(ArrayList<String> path){
String str = null;
ArrayList<Long>listSize = new ArrayList<Long>();
if (path != null) {
for (int i = 0; i < path.size(); i++) {
StatFs statfs = new StatFs(path.get(i));
long blockSize = statfs.getBlockSize();
long availabBlocks = statfs.getAvailableBlocks();
listSize.add(blockSize * availabBlocks);//将所有可使用的内存空间大小放在集合里
Log.i("info", "对应路径"+path.get(i)+"下剩余盘符的大小为:"+String.valueOf(blockSize * availabBlocks));
}
int i = getIndextPath(listSize);
str = path.get(i);
}
return str;
}
/**
* 获取最大使用空间对应的路径索引
* @param list 所有路径下可使用的内存空间大小的集合
* @return 返回有可使用的最大空间路径的下标
*/
private int getIndextPath(ArrayList<Long> list){
int index = 0;
if (list != null) {
for (int dex = 0; dex < list.size(); dex++) {
if (list.size() == 1) {
break;
}else if(list.size()>1){
if (list.size() == (dex + 1)) {
break;
}
if (list.get(dex)>list.get(dex+1)) {
index = dex;
}
}
}
}
return index;
}
/**
* 遍历所有路径下的文件进行查找FollowMe进行匹配,找到返回true,否则返回false
* @param file
* @return 返回匹配的结果
*/
private boolean fileList(File file) {
File[] f1 = file.listFiles();
if (f1 != null) {
for (int i = 1; i < f1.length; i++) {
// 调用递归遍历f1数组中的每一个对象
String[] s = f1[i].getPath().split("/");
if (f1[i].isDirectory()
&& s[s.length - 1].toLowerCase().equals("TianTang")) {
resultFileDir = f1[i].getPath();
flag = true;
break;
}
if (f1[i].isDirectory() && !flag) {
fileList(f1[i]);
}
}
}
return flag;
}
/**
* 读取系统外卡挂载的路径
*/
private static void readMountsFile() {
mMounts.add("/mnt/sdcard");
try {
File localFile = new File("/proc/mounts");
Scanner localScanner = new Scanner(localFile);
while (true) {
if (!localScanner.hasNext())
return;
String str1 = localScanner.nextLine();
if (!str1.startsWith("/dev/block/vold/"))
continue;
String str2 = str1.split(" ")[1];
if (str2.equals("/mnt/sdcard"))
continue;
mMounts.add(str2);
}
} catch (Exception localException) {
while (true)
localException.printStackTrace();
}
}
/**
* 读取系统文件对挂载目录的设置
*/
private static void readVoldFile() {
mVold.add("/mnt/sdcard");
try {
File localFile = new File("/system/etc/vold.fstab");
Scanner localScanner = new Scanner(localFile);
while (true) {
if (!localScanner.hasNext())
return;
String str1 = localScanner.nextLine();
if (!str1.startsWith("dev_mount"))
continue;
String str2 = str1.split(" ")[2];
if (str2.contains(":")) {
int i = str2.indexOf(":");
str2 = str2.substring(0, i);
}
if (str2.equals("/mnt/sdcard"))
continue;
mVold.add(str2);
}
} catch (Exception localException) {
while (true)
localException.printStackTrace();
}
}
/**
* 对直接从挂载的路径下获取外卡的路径和通过读取系统文件获得的挂载路径进行对比
*/
private static void compareMountsWithVold() {
int i = 0;
while (true) {
int j = mMounts.size();
if (i >= j) {
mVold.clear();
return;
}
String str = (String) mMounts.get(i);
if (!mVold.contains(str)) {
ArrayList<String> localArrayList = mMounts;
int k = i + -1;
localArrayList.remove(i);
i = k;
}
i += 1;
}
}
}
2、下面这种,测试的时候返回还是内置卡的路径
Android手机支持SDcard。目前很多手机厂商把SDcard集成到手机中,当然有的手机同时也支持可插拔的SDcard。这就有了内置SDcard和位置SDcard之分。
当手机同时支持内置和外置SDcard时:
调用系统API:Environment.getExternalStorageDirectory().getPath();得到的是SDcard路径为内置的SDcard路径。由于Android系统的碎片话,很多手机厂商处理SDcard的路径都不相同,也没有办法通过/system/etc/vold.fstab文件中的配置信息来确定SDcard的路径,因为这个文件的名字也不唯一。
/**
* 获取外置SD卡路径
*
* @return
*/
public static String getSDCardPath() {
String cmd = "cat /proc/mounts";
Runtime run = Runtime.getRuntime();// 返回与当前 Java 应用程序相关的运行时对象
try {
Process p = run.exec(cmd);// 启动另一个进程来执行命令
BufferedInputStream in = new BufferedInputStream(p.getInputStream());
BufferedReader inBr = new BufferedReader(new InputStreamReader(in));
String lineStr;
while ((lineStr = inBr.readLine()) != null) {
// 获得命令执行后在控制台的输出信息
LOG.i("CommonUtil:getSDCardPath", lineStr);
if (lineStr.contains("sdcard")
&& lineStr.contains(".android_secure")) {
String[] strArray = lineStr.split(" ");
if (strArray != null && strArray.length >= 5) {
String result = strArray[1].replace("/.android_secure",
"");
return result;
}
}
// 检查命令是否执行失败。
if (p.waitFor() != 0 && p.exitValue() == 1) {
// p.exitValue()==0表示正常结束,1:非正常结束
LOG.e("CommonUtil:getSDCardPath", "命令执行失败!");
}
}
inBr.close();
in.close();
} catch (Exception e) {
LOG.e("CommonUtil:getSDCardPath", e.toString());
return Environment.getExternalStorageDirectory().getPath();
}
return Environment.getExternalStorageDirectory().getPath();
}
上面测试的时候,lineStr.contains("sdcard") && lineStr.contains(".android_secure") 这个条件并没有满足
3.其他
3.0以上可以通过反射获取:
StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
// 获取sdcard的路径:外置和内置
String[] paths = (String[]) sm.getClass().getMethod("getVolumePaths", null).invoke(sm, null);
测试过程中,返回的是 系统对挂载路径的设置文件 /system/etc/vold.fstab 中的内容,该文件路径还包括了一下USB的挂载路径。
引用:http://my.eoe.cn/1028320/archive/4718.html
http://stackoverflow.com/questions/5694933/find-an-external-sd-card-location