Android获取SDCard路径/Android获取存储器挂载点

获取Android所有SDCard(存储器)路径

已知获取的方法有二

  • 第一个:反射(Environment/StorageManager、StorageVolume两个思路)

  • 第二个: 读取配置文件”system/etc/vold.fstab”

我们来探究下这些方法,我们的目的就是得到能准确实现功能的方法,(务必提高兼容性,考虑Android版本的差异和手机型号的差异)


1、反射

  • 别人的总结—1(完美实现需求)

    • 我的实现
    /**
    杨铭 Created by Tom on 2016/7/30. 

    Email:[email protected]

    Mobile phone:15133350726

    获取手机挂载点的各种形式

    注意:适用Android版本为[sdk14:sdk23]

    自api16一下的版本在StorageVolume方法中没有getPathFile */ public class GetMountPoint { private Context context; private final static String tag = "GetMountPoint"; /** 构造方法 */ private GetMountPoint(Context context) { this.context = context; } /** 之所以用这种方法初始化时为了防止使用的时候没有检查SDK版本导致出错 */ public static GetMountPoint GetMountPointInstance(Context context) { if (14 <= Build.VERSION.SDK_INT && Build.VERSION.SDK_INT <= 23) { return new GetMountPoint(context); } else { Log.e(tag, "本类不支持当前SDK版本"); return null; } } /** 核心操作-获取所有挂载点信息。 */ public List getMountPoint() { try { Class class_StorageManager = StorageManager.class; Method method_getVolumeList = class_StorageManager.getMethod("getVolumeList"); Method method_getVolumeState = class_StorageManager .getMethod("getVolumeState", String.class); StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE); Class class_StorageVolume = Class.forName("android.os.storage.StorageVolume"); Method method_isRemovable = class_StorageVolume.getMethod("isRemovable"); Method method_getPath = class_StorageVolume.getMethod("getPath"); Method method_getPathFile = null; if (Build.VERSION.SDK_INT >= 17) {// 自api16一下的版本在StorageVolume方法中没有getPathFile method_getPathFile = class_StorageVolume.getMethod("getPathFile"); } Object[] objArray = (Object[]) method_getVolumeList.invoke(sm); //region 所有挂载点File---附带是内置存储卡还是外置存储卡的标志 List result = new ArrayList<>(); for (Object value : objArray) { String path = (String) method_getPath.invoke(value); File file; if (Build.VERSION.SDK_INT >= 17) { file = (File) method_getPathFile.invoke(value); } else { file = new File(path); } boolean isRemovable = (boolean) method_isRemovable.invoke(value); boolean isMounted; String getVolumeState = (String) method_getVolumeState.invoke(sm, path);//获取挂载状态。 if (getVolumeState.equals(Environment.MEDIA_MOUNTED)) { isMounted = true; } else { isMounted = false; } result.add(new MountPoint(file, isRemovable, isMounted)); } return result; //endregion } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } /** 获取处于挂载状态的挂载点的信息 */ public List getMountedPoint() { List result = this.getMountPoint(); for (MountPoint value : result) { if (!value.isMounted) { result.remove(value); } } return result; } public class MountPoint { private File file; /** 用于判断是否为内置存储卡,如果为true就是代表本挂载点可以移除,就是外置存储卡,否则反之 */ private boolean isRemovable; /** 用于标示,这段代码执行的时候这个出处卡是否处于挂载状态,如果是为true,否则反之 */ private boolean isMounted; public MountPoint(File file, boolean isRemovable, boolean isMounted) { this.file = file; this.isMounted = isMounted; this.isRemovable = isRemovable; } public File getFile() { return file; } public boolean isRemovable() { return isRemovable; } public boolean isMounted() { return isMounted; } } }

    结果:

    1. 存储路径:/storage/emulated/0—isRemovable:false—-getVolumeState:mounted
      • 存储路径:/storage/sdcard1—isRemovable:true—-getVolumeState:mounted
      • 存储路径:/storage/usba—isRemovable:true—-getVolumeState:removed
  • 我的总结

    1. 我们根据获取外部存储卡的线索:(来到Environment类)
  File file = Environment.getExternalStorageDirectory();
  1. 找到:
public static File getExternalStorageDirectory() {
    throwIfUserRequired();
    return sCurrentUser.getExternalDirs()[0];
}
  1. 可以看到这里是得到的反馈是一个数组的第一个元素我们继续深入查看:(来到Environment的隐藏一个内部类UserEnvironment
/** {@hide} */
   public static class UserEnvironment {
       sprivate final int mUserId;

       public UserEnvironment(int userId) {
           mUserId = userId;
       }

       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;
       }
       //无关代码若干
     }

  • 到这里我忍不住问一个问题:

    • 这个public File[] getExternalDirs()return到底还包含什么?
    • 解决这个疑问就是我们接下来要做的事情,我们先做出猜测:
    • 【包含的就是这部手机所有的存储路径】
  • 解决这个问题的思路就是通过反射调用这个方法然后亲眼看看结果到底是什么。

  • 具体实施如下:

      • 需要注意的错误:获取静态内部类的类对象
      • Class Class_UserEnvironment = Class.forName("android.os.Environment.UserEnvironment");
        报错java.lang.ClassNotFoundException: android.os.Environment.UserEnvironment
        解决:Class.forName("android.os.Environment$UserEnvironment");

        Class class_UserEnvironment = Class.forName("android.os.Environment")
                                             .getDeclaredClasses()[0];
        Method method_getExternalDirs = class_UserEnvironment.getMethod("getExternalDirs", null);
        //方法的执行需要这个方法所在类的一个对象
        Constructor ueConstructor = class_UserEnvironment
               .getConstructor(new Class[]{Integer.class});
        int mUId = 0;//这个参数还需要处理,暂时先放下
        //这里可以自己初始化,可以以直接通过Environment来调用
        Method method_myUserId = UserHandle.class.getMethod("myUserId", null);
        mUId = (int) method_myUserId.invoke(null, null);//如果底层方法所需的形参数为 0,则所提供的 args 数组长度可以为 0 或 null。
        _Log.e(_Log.msg() + "我们得到的mUID=" + mUId);
        //ueConstructor.newInstance(mUId);这个方法就是获取到类对象的方法
        File[] files = (File[]) method_getExternalDirs.invoke(ueConstructor.newInstance(mUId), null);//以上的一切都是为了这一句服务的
        _Log.e(_Log.msg() + "File[].length=" + files.length);
        for (File value : files)
        {
           _Log.d(_Log.msg() + "-->" + value.getPath());
        }
      • 我们卡在Method method_getExternalDirs = class_UserEnvironment.getMethod("getExternalDirs", null);
        这个位置处了,不能获取到这个Method我的提问帖子

      • 关于这个问题的解决,我认为可能是不同版本的Android系统源码不同造成的,可能我用来测试的机器中根本就没有这个方法。——我接下来的事情就是去验证这个猜测。——已经解决了
      • 深化6.0版本的解决方案。在这里我们无法判断虚拟机是否有外置存储卡,我们如果用5.1.1系统做测试,检测出两个存储卡,说明6.0是区分了内外存储的,我倾向于相信它存在内外存储。——现在我们去测试看看。

    2.根据1.的思路只能正常获取到SDK23-SDK19的路径。我们这里依然贴出代码来。方便以后参考。至于SDK18-SDK14我们有上一个思路来解决(也就是StorageManager、StorageVolume)

    /**
    杨铭 Created by kys_8 on 2016/1/7. 

    Email:[email protected]

    Mobile phone:15133350726

    */
    public class MySDCard { private String tag = this.getClass().getName(); private Context context; /** 构造方法 */ public MySDCard(Context context) { this.context = context; } //region getSDCardPathsVersionID() public File[] getSDCardPaths() { Log.e(tag, "当前手机系统版本号=" + Build.VERSION.SDK_INT); switch (Build.VERSION.SDK_INT) { case 23://23 return this.getSDCardPaths_23(); case 22://22 return this.getSDCardPaths_22(); case 21: return this.getSDCardPaths_21(); case 20: return this.getSDCardPaths_20(); case 19: return this.getSDCardPaths_19(); case 18: return this.getSDCardPaths_18(); case 17: return this.getSDCardPaths_17(); case 16: return this.getSDCardPaths_16(); case 15: return this.getSDCardPaths_15(); case 14: return this.getSDCardPaths_14(); // case 13: // return this.getSDCardPaths_13(); // case 12: // return this.getSDCardPaths_12(); // case 11: // return this.getSDCardPaths_11(); // case 10: // return this.getSDCardPaths_10(); // case 9: // return this.getSDCardPaths_9(); // case 8: // return this.getSDCardPaths_8(); } Log.e(tag, "不支持这个版本,请查阅源码"); return null; } /** 通过使用Environment.getExternalStorageDirectory()所使用的方法得到本机中所有存储卡的位置

    这个总体思路是对的,但是被一些具体的实现给卡住了 */ @TargetApi(23)//这里反射的方法之后api23才有所以我们这个方法是针对这个版本的 public File[] getSDCardPaths_23() { _Log.e(_Log.msg() + "我们得到的mUID=" + Environment.getExternalStorageDirectory().getPath()); try {//我们的思路:首先得到方法-然后执行方法 Class class_UserEnvironment = Class .forName("android.os.Environment$UserEnvironment"); Method method_getExternalDirs = class_UserEnvironment.getMethod("getExternalDirs"); //方法的执行需要这个方法所在类的一个对象 Constructor ueConstructor = class_UserEnvironment .getConstructor(new Class[]{int.class}); //这里可以自己初始化,可以以直接通过Environment来调用 Method method_myUserId = UserHandle.class.getMethod("myUserId"); int mUId = (int) method_myUserId .invoke(null);//如果底层方法所需的形参数为 0,则所提供的 args 数组长度可以为 0 或 null。 _Log.e(_Log.msg() + "我们得到的mUID=" + mUId); //ueConstructor.newInstance(mUId);这个方法就是获取到类对象的方法 File[] files = (File[]) method_getExternalDirs .invoke(ueConstructor.newInstance(mUId));//以上的一切都是为了这一句服务的 _Log.e(_Log.msg() + "File[].length=" + files.length); for (File value : files) { _Log.d(_Log.msg() + "-->" + value.getPath()); } return files; } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return null; } /** 在真机对应版本上进行测试没有任何问题, 但是我还有一个问题,就是为什么在模拟器上就得不到这样一个结果,是因为模拟器的源码和真机的有不同,还是我创建真机的时候有异常? @return 获取到的存储设备 */ @TargetApi(22) public File[] getSDCardPaths_22() { _Log.e(_Log.msg() + "getExternalStorageDirectory=" + Environment .getExternalStorageDirectory().getPath()); try {//我们的思路:初始化内部类,调用方法 Class class_UserEnvironment = Class .forName("android.os.Environment$UserEnvironment"); Method method_getExternalDirsForApp = class_UserEnvironment .getMethod("getExternalDirsForApp"); //方法的执行需要这个方法所在类的一个对象 Constructor ueConstructor = class_UserEnvironment .getConstructor(new Class[]{int.class}); //这里可以自己初始化,可以以直接通过Environment来调用 Method method_myUserId = UserHandle.class.getMethod("myUserId"); int mUId = (int) method_myUserId.invoke(null); _Log.e(_Log.msg() + "我们得到的mUID=" + mUId); //以上的一切都是为了下一句一句服务的 File[] files = (File[]) method_getExternalDirsForApp .invoke(ueConstructor.newInstance(mUId)); _Log.e(_Log.msg() + "File[].length=" + files.length); for (File value : files) { //boolean isRemovable = (boolean) method_isRemovable.invoke(value);//是否可以移除 //String getVolumeState = (String) method_getVolumeState.invoke(sm, path);//获取挂载状态。 _Log.d(_Log.msg() + "-->" + value.getPath()); } return files; } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return null; } /** 如果调用高版本的实现方式,说明这两个版本的我们用到的源码是相同的。 @return 获取到的存储设备 */ @TargetApi(21) public File[] getSDCardPaths_21() { return getSDCardPaths_22(); } /** 如果调用高版本的实现方式,说明这两个版本的我们用到的源码是相同的。 @return 获取到的存储设备 */ @TargetApi(20) public File[] getSDCardPaths_20() { return getSDCardPaths_22(); } /** 如果调用高版本的实现方式,说明这两个版本的我们用到的源码是相同的。 @return 获取到的存储设备 */ @TargetApi(19) public File[] getSDCardPaths_19() { return getSDCardPaths_22(); } /** 如果调用高版本的实现方式,说明这两个版本的我们用到的源码是相同的。

    借鉴:http://vjson .com/wordpress/%E8%8E%B7%E5%8F%96android%E8%AE%BE%E5%A4%87%E6%8C%82%E8%BD%BD %E7%9A%84%E6%89%80%E6%9C%89%E5%AD%98%E5%82%A8%E5%99%A8.html @return 获取到的存储设备 */ @TargetApi(18) public File[] getSDCardPaths_18() { try { _Log.e(_Log.msg() + "getExternalStorageDirectory=" + Environment .getExternalStorageDirectory().getPath()); Class class_StorageManager = StorageManager.class; Method method_getVolumeList = class_StorageManager.getMethod("getVolumeList"); Method method_getVolumeState = class_StorageManager .getMethod("getVolumeState", String.class); StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE); Class class_StorageVolume = Class.forName("android.os.storage.StorageVolume"); Method method_isRemovable = class_StorageVolume.getMethod("isRemovable"); Method method_getPath = class_StorageVolume.getMethod("getPath"); Method method_getPathFile = class_StorageVolume.getMethod("getPathFile"); Object[] objArray = (Object[]) method_getVolumeList.invoke(sm); _Log.e(_Log.msg() + "objArray[].length=" + objArray.length + "---根据是否可以移除来判断是否为外置存储卡。"); List fileList = new ArrayList<>(); for (Object value : objArray) { String path = (String) method_getPath.invoke(value); boolean isRemovable = (boolean) method_isRemovable.invoke(value); String getVolumeState = (String) method_getVolumeState.invoke(sm, path);//获取挂载状态。 _Log.d(_Log.msg() + "存储路径:" + path + "---isRemovable:" + isRemovable + "----getVolumeState:" + getVolumeState); fileList.add((File) method_getPathFile.invoke(value)); } File[] files = (File[]) fileList.toArray(); return files; } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } /** 如果调用高版本的实现方式,说明这两个版本的我们用到的源码是相同的。 @return 获取到的存储设备 */ @TargetApi(17) public File[] getSDCardPaths_17() { return getSDCardPaths_18(); } /** 如果调用高版本的实现方式,说明这两个版本的我们用到的源码是相同的。

    api16在StorageVolume方法中没有getPathFile @return 获取到的存储设备 */ @TargetApi(16) public File[] getSDCardPaths_16() { try { _Log.e(_Log.msg() + "getExternalStorageDirectory=" + Environment .getExternalStorageDirectory().getPath()); Class class_StorageManager = StorageManager.class; Method method_getVolumeList = class_StorageManager.getMethod("getVolumeList"); Method method_getVolumeState = class_StorageManager .getMethod("getVolumeState", String.class); StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE); Class class_StorageVolume = Class.forName("android.os.storage.StorageVolume"); Method method_isRemovable = class_StorageVolume.getMethod("isRemovable"); Method method_getPath = class_StorageVolume.getMethod("getPath"); Object[] objArray = (Object[]) method_getVolumeList.invoke(sm); _Log.e(_Log.msg() + "objArray[].length=" + objArray.length + "---根据是否可以移除来判断是否为外置存储卡。"); List fileList = new ArrayList<>(); for (Object value : objArray) { String path = (String) method_getPath.invoke(value); boolean isRemovable = (boolean) method_isRemovable.invoke(value); String getVolumeState = (String) method_getVolumeState.invoke(sm, path);//获取挂载状态。 _Log.d(_Log.msg() + "存储路径:" + path + "---isRemovable:" + isRemovable + "----getVolumeState:" + getVolumeState); fileList.add(new File((String) method_getPath.invoke(value))); } File[] files = (File[]) fileList.toArray(); return files; } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } /** 如果调用高版本的实现方式,说明这两个版本的我们用到的源码是相同的。 @return 获取到的存储设备 */ @TargetApi(15) public File[] getSDCardPaths_15() { return getSDCardPaths_16(); } /** 如果调用高版本的实现方式,说明这两个版本的我们用到的源码是相同的。 @return 获取到的存储设备 */ @TargetApi(14) public File[] getSDCardPaths_14() { return getSDCardPaths_16(); } //endregion }

    这两段代码都是查看源码写的,在模拟器真机测试没能大面积尝试。如果有错误还希望指出,并同时指明手机型号


参考

  1. 获取Android设备挂载的所有存储器
  2. 有提到的方法种类比较多

点击下载


最新android6.0思路代码:
    //http://blog.csdn.net/kc58236582/article/details/50524543
    @TargetApi(23)
    private void test23()
    {
        try
        {
            Class class_StorageManager = StorageManager.class;
            StorageManager storageManager = (StorageManager) this
                    .getSystemService(Context.STORAGE_SERVICE);
            Method method_getVolumes = class_StorageManager.getMethod("getVolumes");
            Method method_getDisks = class_StorageManager.getMethod("getDisks");

            Class class_VolumeInfo = Class.forName("android.os.storage.VolumeInfo");
            Method method_getPath = class_VolumeInfo.getMethod("getPath");
            Method method_getDisk = class_VolumeInfo.getMethod("getDisk");

            Class class_DiskInfo = Class.forName("android.os.storage.DiskInfo");
            Method method_isSd = class_DiskInfo.getMethod("isSd");
            Method method_isUsb = class_DiskInfo.getMethod("isUsb");

            List volumes = (List) method_getVolumes.invoke(storageManager);

            for (Object volumeInfo : volumes)
            {
                File file = (File) method_getPath.invoke(volumeInfo);
                _Log.e(_Log.msg() + "file=" + file);
                if (file != null)
                {
                    Object diskInfo = method_getDisk.invoke(volumeInfo);
                    if (diskInfo != null)
                    {
                        boolean isSd = (boolean) method_isSd.invoke(diskInfo);
                        boolean isUsb = (boolean) method_isUsb.invoke(diskInfo);
                        _Log.e(_Log.msg() + _Log.likeCoordinate("[isSd,isUsb]", isSd, isUsb));
                    }
                    else
                    {
                        _Log.e(_Log.msg() + "diskInfo == null:" + true);
                    }
                }
            }

            _Log.w(_Log.msg() + "888888888888888888888888888888888");
            List disks = (List) method_getDisks.invoke(storageManager);
            _Log.i(_Log.msg() + "disks.size()=" + disks.size());
        }
        catch (NoSuchMethodException e)
        {
            e.printStackTrace();
        }
        catch (ClassNotFoundException e)
        {
            e.printStackTrace();
        }
        catch (InvocationTargetException e)
        {
            e.printStackTrace();
        }
        catch (IllegalAccessException e)
        {
            e.printStackTrace();
        }
    }






你可能感兴趣的:(Android工具类)