SD卡分为内置SD卡和外置SD卡,在平时的工作很少会用到外置的SD卡,因为很多的手机不支持外置SD卡,但也有一些特殊的需求会用到外置的SD卡,不然google也不会对这方面做相应的处理,那么我们怎么才能准确的获取外置SD卡的路径呢?
方法一:(在API 23之前)
1.获取内置SD卡的路径:
String sdcardPath1 = System.getenv("EXTERNAL_STORAGE");
String sdcardPath2 = Environment.getExternalStorageDirectory().getAbsolutePath();
2.获取外置SD卡的路径:
String extSdcardPath = System.getenv("SECONDARY_STORAGE");
这里可能会有很多人去尝试一下这几种方法,博主也不列外,但是你看会发现sdcardPath1和sdcardPath2的路径会不一样,如sdcardPath1=/storage/emulated/legacy;
sdcardPath2=/storage/emulated/0;
这是为什么?不要担心,这是Android系统的问题,android 4.2使用fuse技术/dev/fuse 会被挂载到/storage/emulated/0 目录,为了兼容以前的版本,同时挂载到 /storage/emulated/legacy (故名思议,传统的),还建立三个软连接 /storage/sdcard0 ,/sdcard,/mnt/sdcard ,都指向 /storage/emulated/legacy。也就是说文件夹0和legacy指向的地址是一致的,都是存储盘里的。
在Enviroment类的源码中获得sd卡路径其实也是通过 System.getnv() 方法来实现的.在Environment的内部类UserEnvironment类的构造方法中:
String rawExternalStorage = System.getenv(ENV_EXTERNAL_STORAGE);
final String rawSecondaryStorage = System.getenv(ENV_SECONDARY_STORAGE);
注意:在API 23版本或者更高,里面的ENV_SECONDARY_STORAGE被移除了。
方法二:
Method getVolumList = StorageManager.class.getMethod("getVolumeList");
getVolumList.setAccessible(true);
Object[] results = (Object[])getVolumList.invoke(sm);
if (results != null) {
for (Object result : results) {
// 判断storage volume是否可以移除,true为外置SD卡,false为内置SD卡
Method mRemoveable = result.getClass().getMethod("isRemovable");
Boolean isRemovable = (Boolean) mRemoveable.invoke(result);
if (isRemovable) {
// 获取外置SD卡的路径
Method getPath = result.getClass().getMethod("getPath");
String path = (String) mRemoveable.invoke(result);
// 获取外置SD卡的挂载状态
Method getState = sm.getClass().getMethod("getVolumeState", String.class);
String state = (String)getState.invoke(sm, path);
if (state.equals(Environment.MEDIA_MOUNTED)) {
isMounted = true;
break;
}
}
}
}
代码是通过反射获取的,因为在StorageManager源码中这些方法是被hide的。代码已经注释了。
而获取内置的SD卡的路径还是一样,接口没变,只不过里面的实现有点变了。就拿Environment.getExternalStorageDirectory()
来说,因为最终的实现也是通过String sdcardPath1 = System.getenv("EXTERNAL_STORAGE")
上面已经说了。直接上源码:
public static File getExternalStorageDirectory() {
throwIfUserRequired();
return sCurrentUser.getExternalDirs()[0];
}
可以看到他是通过sCurrentUser.getExternalDirs()[0]获取的,sCurrentUser是UserEnvironment的实例,那我们来看看getExternalDirs()方法:
API 23之后:
public File[] getExternalDirs() {
final StorageVolume[] volumes = StorageManager.getVolumeList(mUserId,
StorageManager.FLAG_FOR_WRITE);
final File[] files = new File[volumes.length];
for (int i = 0; i < volumes.length; i++) {
files[i] = volumes[i].getPathFile();
}
return files;
}
他也是通过getVolumeList方法获取所有的StorageVolume,然后通过getPathFile()获取路径,是一个数组,file[0]是代表内置SD卡的。
而在API 23之前:
public static File getExternalStorageDirectory() {
throwIfUserRequired();
return sCurrentUser.getExternalDirsForApp()[0];
}
他是通过getExternalDirsForApp()方法获取的,我们就来看看这个方法:
public File[] getExternalDirsForApp() {
return mExternalDirsForApp;
}
而mExternalDirsForApp是在UserEnvironment的构造方法中初始化的。
mExternalDirsForApp = externalForApp.toArray(new File[externalForApp.size()]);
网上还有一种方法可以获取所有存储空间的路径的方法:就是通过getStorageDirectories()获取,这个没找到,还请见谅。
这里面可能会有很多的问题,欢迎指出,一起学习讨论。