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

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开发
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

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

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭