当前位置: 萬仟网 > IT编程>移动开发>Android > Android 获取内外SD卡路径几种方法总结

Android 获取内外SD卡路径几种方法总结

2019年07月24日  | 萬仟网IT编程  | 我要评论

android 获取sd卡路径:

外置sd卡路径,也许很多同学在平时的工作中并不会用到,因为现在很多机型都不支持外置sd卡(这也是google目标),所以并不用考虑外置sd卡的路径问题。除了开发文件管理类的应用之外,其他应用使用 enviroment 这个类中的一些静态方法就能满足需要。但也有一些特殊需求需要用到外置sd卡路径,那怎么才能准确获得外置sd卡的路径呢?

方法一

//内置sd卡路径
string sdcardpath = system.getenv("external_storage"); 
//内置sd卡路径
string sdcardpath = environment.getexternalstoragedirectory().getabsolutepath();
//外置置sd卡路径
string extsdcardpath = system.getenv("secondary_storage");
在enviroment类的源码中获得sd卡路径其实也是通过 system.getnv() 方法来实现的,如隐藏的方法:

/** {@hide} */
public static file getlegacyexternalstoragedirectory() {
  return new file(system.getenv(env_external_storage));
} 

注:更详细的内容还是去看enviroment源码。

另外要注意的是,在api 23版本中 secondary_storage 被移除。

方法二

private static string getstoragepath(context mcontext, boolean is_removale) { 

   storagemanager mstoragemanager = (storagemanager) mcontext.getsystemservice(context.storage_service);
    class<?> storagevolumeclazz = null;
    try {
      storagevolumeclazz = class.forname("android.os.storage.storagevolume");
      method getvolumelist = mstoragemanager.getclass().getmethod("getvolumelist");
      method getpath = storagevolumeclazz.getmethod("getpath");
      method isremovable = storagevolumeclazz.getmethod("isremovable");
      object result = getvolumelist.invoke(mstoragemanager);
      final int length = array.getlength(result);
      for (int i = 0; i < length; i++) {
        object storagevolumeelement = array.get(result, i);
        string path = (string) getpath.invoke(storagevolumeelement);
        boolean removable = (boolean) isremovable.invoke(storagevolumeelement);
        if (is_removale == removable) {
          return path;
        }
      }
    } catch (classnotfoundexception e) {
      e.printstacktrace();
    } catch (invocationtargetexception e) {
      e.printstacktrace();
    } catch (nosuchmethodexception e) {
      e.printstacktrace();
    } catch (illegalaccessexception e) {
      e.printstacktrace();
    }
    return null;
}

通过反射的方式使用在sdk中被 隐藏 的类 stroagevolume 中的方法getvolumelist(),获取所有的存储空间(stroage volume),然后通过参数is_removable控制,来获取内部存储和外部存储(内外sd卡)的路径,参数 is_removable为false时得到的是内置sd卡路径,为true则为外置sd卡路径。

在api 23 enviroment 类中的内部类 userenvironment 中有一方法getexternaldirs与此一样,代码如下:

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

再看enviroment的getexternalstoragedirectory方法实现:

public static file getexternalstoragedirectory() {
  throwifuserrequired();
  return scurrentuser.getexternaldirs()[0];
}

可以看出,在api 23时,先是通过getexternaldirs()获取到所有存储空间的file[]数组,这个数组的第一个值:getexternaldirs()[0],即为内置sd卡所在路径。

而在api 23 之前的版本中,并没有类似getexternaldirs()的方法通过storagevolume直接获得存储空间(storage volume),而时通过别的方式来实现的,看关键方法的源码:

public static file getexternalstoragedirectory() {
  throwifuserrequired();
  return scurrentuser.getexternaldirsforapp()[0];
}

这里的 getexternaldirsforapp() 和上面的 getexternaldirs() 的作用是一样的,都是得到所有存储空间的file[]数组。

public file[] getexternaldirsforapp() {
  return mexternaldirsforapp;
}

mexternaldirsforapp 是在 enviroment 类中的内部类 userenvironment 的构造方法中初始化的,enviroment#userenvironment构造函数源码如下:

  public userenvironment(int userid) {
      // see storage config details at http://source.android.com/tech/storage/
      string rawexternalstorage = system.getenv(env_external_storage);
      string rawemulatedsource = system.getenv(env_emulated_storage_source);
      string rawemulatedtarget = system.getenv(env_emulated_storage_target);

      string rawmediastorage = system.getenv(env_media_storage);
      if (textutils.isempty(rawmediastorage)) {
        rawmediastorage = "/data/media";
      }

      arraylist<file> externalforvold = lists.newarraylist();
      arraylist<file> externalforapp = lists.newarraylist();

      if (!textutils.isempty(rawemulatedtarget)) {
        // device has emulated storage; external storage paths should have
        // userid burned into them.
        final string rawuserid = integer.tostring(userid);
        final file emulatedsourcebase = new file(rawemulatedsource);
        final file emulatedtargetbase = new file(rawemulatedtarget);
        final file mediabase = new file(rawmediastorage);

        // /storage/emulated/0
        externalforvold.add(buildpath(emulatedsourcebase, rawuserid));
        externalforapp.add(buildpath(emulatedtargetbase, rawuserid));
        // /data/media/0
        memulateddirfordirect = buildpath(mediabase, rawuserid);

      } else {
        // device has physical external storage; use plain paths.
        if (textutils.isempty(rawexternalstorage)) {
          log.w(tag, "external_storage undefined; falling back to default");
          rawexternalstorage = "/storage/sdcard0";
        }

        // /storage/sdcard0
        externalforvold.add(new file(rawexternalstorage));
        externalforapp.add(new file(rawexternalstorage));
        // /data/media
        memulateddirfordirect = new file(rawmediastorage);
      }

      // splice in any secondary storage paths, but only for owner
      final string rawsecondarystorage = system.getenv(env_secondary_storage);
      if (!textutils.isempty(rawsecondarystorage) && userid == userhandle.user_owner) {
        for (string secondarypath : rawsecondarystorage.split(":")) {
          externalforvold.add(new file(secondarypath));
          externalforapp.add(new file(secondarypath));
        }
      }

      mexternaldirsforvold = externalforvold.toarray(new file[externalforvold.size()]);
      mexternaldirsforapp = externalforapp.toarray(new file[externalforapp.size()]);
    }

也可以根据这个方法得到一个获取所有存储空间的路径的方法getstoragedirectories():

/**
   * returns all available sd-cards in the system (include emulated)
   * <p/>
   * warning: hack! based on android source code of version 4.3 (api 18)
   * because there is no standard way to get it.
   * todo: test on future android versions 4.4+
   *
   * @return paths to all available sd-cards in the system (include emulated)
   */
  private static final pattern dir_separator = pattern.compile("/");
  public list<string> getstoragedirectories() {
    // final set of paths
    final arraylist<string> rv = new arraylist<string>();
    // primary physical sd-card (not emulated)
    final string rawexternalstorage = system.getenv("external_storage");
    // all secondary sd-cards (all exclude primary) separated by ":"
    final string rawsecondarystoragesstr = system.getenv("secondary_storage");
    // primary emulated sd-card
    final string rawemulatedstoragetarget = system.getenv("emulated_storage_target");
    if (textutils.isempty(rawemulatedstoragetarget)) {
      // device has physical external storage; use plain paths.
      if (textutils.isempty(rawexternalstorage)) {
        // external_storage undefined; falling back to default.
        rv.add("/storage/sdcard0");
      } else {
        rv.add(rawexternalstorage);
      }
    } else {
      // device has emulated storage; external storage paths should have
      // userid burned into them.
      final string rawuserid;
      if (build.version.sdk_int < build.version_codes.jelly_bean_mr1) {
        rawuserid = "";
      } else {
        final string path = environment.getexternalstoragedirectory().getabsolutepath();
        final string[] folders = dir_separator.split(path);
        final string lastfolder = folders[folders.length - 1];
        boolean isdigit = false;
        try {
          integer.valueof(lastfolder);
          isdigit = true;
        } catch (numberformatexception ignored) {
        }
        rawuserid = isdigit ? lastfolder : "";
      }
      // /storage/emulated/0[1,2,...]
      if (textutils.isempty(rawuserid)) {
        rv.add(rawemulatedstoragetarget);
      } else {
        rv.add(rawemulatedstoragetarget + file.separator + rawuserid);
      }
    }
    // add all secondary storages
    if (!textutils.isempty(rawsecondarystoragesstr)) {
      // all secondary sd-cards splited into array
      final string[] rawsecondarystorages = rawsecondarystoragesstr.split(file.pathseparator);
      collections.addall(rv, rawsecondarystorages);
    }
    rootmode = sp.getboolean("rootmode", false);
    if (rootmode)
      rv.add("/");
    file usb = getusbdrive();
    if (usb != null && !rv.contains(usb.getpath())) rv.add(usb.getpath());

    return rv;
  }
public file getusbdrive() {
    file parent;
    parent = new file("/storage");

    try {
      for (file f : parent.listfiles()) {
        if (f.exists() && f.getname().tolowercase().contains("usb") && f.canexecute()) {
          return f;
        }
      }
    } catch (exception e) {
    }
    parent = new file("/mnt/sdcard/usbstorage");
    if (parent.exists() && parent.canexecute())
      return (parent);
    parent = new file("/mnt/sdcard/usb_storage");
    if (parent.exists() && parent.canexecute())
      return parent;

    return null;
  }

综上分析,通过方法一和方法二都可以正确的获取内外sd卡路径,但方法一会存在以下问题:

1、api>=23 时方法一无效(暂未测试)

2、有些厂商的rom改动太多,对相关原生api的支持存在问题,这时方法一可能会存在问题。

3、其他一些情况造成的原因(基本与2差不多,是rom等因素造成的)

所以,在使用时建议使用方法二来获取内外置sd卡路径,在api 23(android 6.0)之前使用getstoragedirectories() 应该也是ok的。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
Copyright © 2020  萬仟网 保留所有权利. 粤ICP备17035492号-1
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com