获取Android设备上的所有存储设备

原创 2016年05月16日 18:08:18

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);
    }

系统提供了StorageManager类,它有一个方法叫getVolumeList(),这个方法的返回值是一个StorageVolume数组,StorageVolume类中封装了挂载路径,挂载状态,以及是否可以移除等信息。下面是这个方法的源码。

    /**
     * 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;
        }
    }

getVolumeList()方法是隐藏的,不能在应用代码中直接调用,所以我们只能通过反射来调用这个方法了。


通过反射机制获取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<StorageInfo> listAllStorage(Context context) {
		ArrayList<StorageInfo> storages = new ArrayList<StorageInfo>();
		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<StorageInfo> getAvaliableStorage(List<StorageInfo> infos){
		List<StorageInfo> storages = new ArrayList<StorageInfo>();
		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<StorageInfo> list = listAllStorage(this);
	for(StorageInfo info : list){
		Log.e(TAG, info.toString());
	}
	Log.e(TAG, "-----------------");
	List<StorageInfo> 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"/> ,否则获取不到存储设备。



Android获得所有存储设备位置最佳方式

本方式可以获得内部存储设备地址、SD卡地址、USB设备地址,兼容性能达到99%(别问我为什么这么保证,因为是借鉴了Android设置->存储页面的源码)。 由于调用了几个被@hide的方法,所以采用...
  • baidu_28136669
  • baidu_28136669
  • 2017年03月08日 18:17
  • 1868

Android 存储设备管理 -- StorageManager

上图关系为:     StorageManager为Client,MountService是Server,通过AIDL进行进程间通信。    MountService是一个Android S...
  • muojie
  • muojie
  • 2012年12月25日 11:10
  • 3554

Android StorageManager 存储设备管理

前言          欢迎大家我分享和推荐好用的代码段~~ 声明          欢迎转载,但请保留文章原始出处:          CSDN:http://www.csdn.net       ...
  • luckkof
  • luckkof
  • 2013年10月28日 14:54
  • 2602

android 判断手机中是否有SD卡,USB。静态判断

public static String getSDPath(Context context) { String sd = null; StorageManager s...
  • lisineng
  • lisineng
  • 2015年08月29日 09:34
  • 3857

Android 获取内置和外置存储卡的路径及总共、可用空间

Android 获取内置外置内存卡的路径及总共、可用空间
  • gaolei1201
  • gaolei1201
  • 2016年07月12日 18:47
  • 5441

Android 获取外部存储设备列表

由于不同芯片设备的外部存储设备不太统一,比如说外部sdcard,u盘等,他们的路径在不同芯片上面的定义是不一样的,所以我们需要动态的去获取机器当前的外部设备mount 的情况Android 通过 St...
  • Android_grady_wang
  • Android_grady_wang
  • 2016年10月19日 16:32
  • 1090

获取手机和SD卡路径(Android 4.0以上验证过)

StorageManager mStorageManager = (StorageManager)getApplicationContext().getSystemService("storage")...
  • rvncds
  • rvncds
  • 2014年04月03日 17:31
  • 1348

Android外部存储/内部存储路径获取大全

package com.mufeng.toolproject.utils; import android.content.Context; import android.os.Environment...
  • nimeghbia
  • nimeghbia
  • 2017年03月20日 09:03
  • 6714

如何获得Android设备名称(ADB命令详细介绍)

豌豆荚、360手机管家等软件可以获取Android设备名称,显示在界面上,如下图: 我们自己如何来获取设备名称 呢?答案如下: 在命令行中输入“adb shell” 进入shell之后,...
  • cbbbc
  • cbbbc
  • 2016年10月22日 14:45
  • 4568

《Android-查看手机内CPU手机型号设备信息等以及adb常用命令》---记录几个命令

查看设备信息build.prop里面还有好多其他的信息root@dior:/data # cat /system/build.prop | grep "product" cat /system/bui...
  • w695050167
  • w695050167
  • 2017年06月21日 14:25
  • 2960
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:获取Android设备上的所有存储设备
举报原因:
原因补充:

(最多只允许输入30个字)