Android系统提供了Environment.getExternalStorageDirectory()接口获得存储设备的路径,但是这个接口往往给出的结果并不是我们想要的,在某些设备上它返回的是手机内部存储,某些设备上返回的手机外部存储。还有就是某些Android设备支持扩展多个sdcard,这个时候想要获得所有存储器的挂载路径,这个接口是没有办法办到的。
那么,Android系统的文件管理器是如何把所有挂载的存储设备加载出来的呢?通过查看文件管理器的源码发现是在MountPointManager类中处理的,通过调用StorageManager类的getVolumeList()方法获取的。
/**
* This method initializes MountPointManager.
*
* @param context Context to use
*/
public void init(Context context) {
mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
final String defaultPath = getDefaultPath();
LogUtils.d(TAG, "init,defaultPath = " + defaultPath);
if (!TextUtils.isEmpty(defaultPath)) {
mRootPath = ROOT_PATH;
}
mMountPathList.clear();
// check media availability to init mMountPathList
StorageVolume[] storageVolumeList = mStorageManager.getVolumeList();
if (storageVolumeList != null) {
for (StorageVolume volume : storageVolumeList) {
MountPoint mountPoint = new MountPoint();
mountPoint.mDescription = volume.getDescription(context);
mountPoint.mPath = volume.getPath();
mountPoint.mIsMounted = isMounted(volume.getPath());
mountPoint.mIsExternal = volume.isRemovable();
mountPoint.mMaxFileSize = volume.getMaxFileSize();
LogUtils.d(TAG, "init,description :" + mountPoint.mDescription + ",path : "
+ mountPoint.mPath + ",isMounted : " + mountPoint.mIsMounted
+ ",isExternal : " + mountPoint.mIsExternal + ", mMaxFileSize: " + mountPoint.mMaxFileSize);
mMountPathList.add(mountPoint);
}
}
IconManager.getInstance().init(context, defaultPath + SEPARATOR);
}
/**
* Returns list of all mountable volumes.
* @hide
*/
public StorageVolume[] getVolumeList() {
if (mMountService == null) return new StorageVolume[0];
try {
Parcelable[] list = mMountService.getVolumeList();
if (list == null) return new StorageVolume[0];
int length = list.length;
StorageVolume[] result = new StorageVolume[length];
for (int i = 0; i < length; i++) {
result[i] = (StorageVolume)list[i];
}
return result;
} catch (RemoteException e) {
Log.e(TAG, "Failed to get volume list", e);
return null;
}
}
通过反射机制获取Android设备的所有存储设备
public class StorageInfo {
public String path;
public String state;
public boolean isRemoveable;
public StorageInfo(String path) {
this.path = path;
}
public boolean isMounted() {
return "mounted".equals(state);
}
@Override
public String toString() {
return "StorageInfo [path=" + path + ", state=" + state
+ ", isRemoveable=" + isRemoveable + "]";
}
}
public static List listAllStorage(Context context) {
ArrayList storages = new ArrayList();
StorageManager storageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
try {
Class>[] paramClasses = {};
Method getVolumeList = StorageManager.class.getMethod("getVolumeList", paramClasses);
Object[] params = {};
Object[] invokes = (Object[]) getVolumeList.invoke(storageManager, params);
if (invokes != null) {
StorageInfo info = null;
for (int i = 0; i < invokes.length; i++) {
Object obj = invokes[i];
Method getPath = obj.getClass().getMethod("getPath", new Class[0]);
String path = (String) getPath.invoke(obj, new Object[0]);
info = new StorageInfo(path);
Method getVolumeState = StorageManager.class.getMethod("getVolumeState", String.class);
String state = (String) getVolumeState.invoke(storageManager, info.path);
info.state = state;
Method isRemovable = obj.getClass().getMethod("isRemovable", new Class[0]);
info.isRemoveable = ((Boolean) isRemovable.invoke(obj, new Object[0])).booleanValue();
storages.add(info);
}
}
} catch (Exception e) {
e.printStackTrace();
}
storages.trimToSize();
return storages;
}
public static List getAvaliableStorage(List infos){
List storages = new ArrayList();
for(StorageInfo info : infos){
File file = new File(info.path);
if ((file.exists()) && (file.isDirectory()) && (file.canWrite())) {
if (info.isMounted()) {
storages.add(info);
}
}
}
return storages;
}
调用上述方法:
List list = listAllStorage(this);
for(StorageInfo info : list){
Log.e(TAG, info.toString());
}
Log.e(TAG, "-----------------");
List infos = getAvaliableStorage(list);
for(StorageInfo info : infos){
Log.e(TAG, info.toString());
}
Log.e(TAG, "Environment.getExternalStorageDirectory(): " + Environment.getExternalStorageDirectory());
连上手机进行验证,输出Log信息:
可以看到,通过listAllStorage()方法获取到了手机上的所有存储设备,通过getAvaliableStorage()方法的过滤获取到了挂载状态的所有存储设备。由于该手机只有一个可读写的存储设备,因此与Environment.getExternalStorageDirectory()方法获取到的结果一致。
注意:由于在getAvaliableStorage()方法中我们获取的是可写(canWrite)的设备,需要加上相应权限: <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> ,否则获取不到存储设备。