有需求做一些类似文件管理器的,就会用到获取外置可移动SD卡的路径。一般的通过Environment或者Context获取的都是手机自带的存储卡路径,类似storage/emulated/0/加后缀。由于谷歌之后的意思是像ios一样,不支持外置USB或者外置可移动SD存储。但是国内的厂商一般都支持。先大概分个类,6.0以下的使用方法一,6.0以上的使用方法二。
Android10 三星S10亲测如下方法获取外置SD卡路径有效:
public String externalSDCardPath() {
String externalSDCardPath = "";
try {
StorageManager storageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
// 7.0才有的方法
List<StorageVolume> storageVolumes = storageManager.getStorageVolumes();
Class<?> volumeClass = Class.forName("android.os.storage.StorageVolume");
Method getPath = volumeClass.getDeclaredMethod("getPath");
Method isRemovable = volumeClass.getDeclaredMethod("isRemovable");
getPath.setAccessible(true);
isRemovable.setAccessible(true);
for (int i = 0; i < storageVolumes.size(); i++) {
StorageVolume storageVolume = storageVolumes.get(i);
String mPath = (String) getPath.invoke(storageVolume);
Boolean isRemove = (Boolean) isRemovable.invoke(storageVolume);
if(isRemove){
externalSDCardPath = mPath;
}
Log.i("tag2", "mPath is === " + mPath + "isRemoveble == " + isRemove);
}
}catch (Exception e){
Log.i("tag2","e == "+e.getMessage());
}
return externalSDCardPath;
}
方法一
1.遍历env的key里面有SECOENDARY_STORAGE这个值,代表是第二储存,即为外置可移动SD卡。EXTERNAL_STORAGE则对应的是手机内部的存储。在华为荣耀6(4.4)上测试的结果是: 外置 storage/sdcard1 , 内置 storage/emulated/0 。自测了一下,String inpath= System.getenv(“EXTERNAL_STORAGE”)获取内置sd卡,和系统的 Environment.getExternalStorageDirectory().getAbsolutePath()获取的结果是一样的
private static void initSDCardPath() {
try {
if(TextUtils.isEmpty(exterPath) && TextUtils.isEmpty(innerPath)){
Map<String, String> map = System.getenv();
Set<String> set = System.getenv().keySet();
Iterator<String> keys = set.iterator();
while (keys.hasNext()) {
String key = keys.next();
String value = map.get(key);
if ("SECONDARY_STORAGE".equals(key)) {
if(!TextUtils.isEmpty(value) && value.contains(":")){
exterPath = value.split(":")[0];
}else{
exterPath = value;
}
}
if ("EXTERNAL_STORAGE".equals(key)) {
innerPath = value;
}
if(!TextUtils.isEmpty(exterPath) && !TextUtils.isEmpty(innerPath)){
break;
}
}
LogUtil.i("file browser", "exterPath= "+exterPath+";innerPath="+innerPath);
resetDownloadPath();
}
} catch (Exception e) {
}
}
方法二
1.这个适用6.0+的版本,因为谷歌在6.0+移除了secondary_storage这个值,所以需要另辟蹊径。查看Environment的源码则可以找到答案,
public static File getExternalStorageDirectory() {
throwIfUserRequired();
return sCurrentUser.getExternalDirs()[0];
}
//
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;
}
所有要获取所有的存储路径,就要用到storageManager,这个类可以通过以下代码获取
StorageManager storageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
然后查看getVolumeList ,getVolumePaths,或者getStorageVolumes(7.0 api)。这几个方法是@hide不对外使用的,所以用反射就可以了。
/**
* 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;
}
}
/**
* Returns list of paths for all mountable volumes.
* @hide
*/
public String[] getVolumePaths() {
StorageVolume[] volumes = getVolumeList();
if (volumes == null) return null;
int count = volumes.length;
String[] paths = new String[count];
for (int i = 0; i < count; i++) {
paths[i] = volumes[i].getPath();
}
return paths;
}
/**
* Return the list of shared/external storage volumes available to the
* current user. This includes both the primary shared storage device and
* any attached external volumes including SD cards and USB drives.
*
* @see Environment#getExternalStorageDirectory()
* @see StorageVolume#createAccessIntent(String)
*/
public @NonNull List<StorageVolume> getStorageVolumes() {
final ArrayList<StorageVolume> res = new ArrayList<>();
Collections.addAll(res,
getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE));
return res;
}
以下是具体用反射获取外置可移动SD卡路径的方法,在8.0华为Nova2s测试的结果是:外置/storage/0403-0201 内置/storage/emulated/0 . 注意/storage/0403-0201可能跟机型有关,具体的看实际Log. removable为true则意味着这个路径是外置可移动的,即为外置SD卡路径
public String externalSDCardPath() {
try {
StorageManager storageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
// 7.0才有的方法
List<StorageVolume> storageVolumes = storageManager.getStorageVolumes();
Class<?> volumeClass = Class.forName("android.os.storage.StorageVolume");
Method getPath = volumeClass.getDeclaredMethod("getPath");
Method isRemovable = volumeClass.getDeclaredMethod("isRemovable");
getPath.setAccessible(true);
isRemovable.setAccessible(true);
for (int i = 0; i < storageVolumes.size(); i++) {
StorageVolume storageVolume = storageVolumes.get(i);
String mPath = (String) getPath.invoke(storageVolume);
Boolean isRemove = (Boolean) isRemovable.invoke(storageVolume);
Log.d("tag2", "mPath is === " + mPath + "isRemoveble == " + isRemove);
}
}catch (Exception e){
Log.d("tag2","e == "+e.getMessage());
}
return "";
}
**
补充一点,反射StorageManager获取外置路径使用到了StorageVolume, 但此类在7.0才放开使用,所以编译版本低于24的需要下面的方法。
/**
* 6.0使用此方法获取外置SD卡路径,尝试过反射{@link StorageManager#getVolumeList}
* 但StorageVolume非Public API 编译不通过(7.0改为公开API),故使用UserEnvironment
* 的内部方法getExternalDirs获取所有的路径,通过{@link Environment#isExternalStorageRemovable(File)}
* 判断若removable则为外部存储
*/
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private static String getPhysicalExternalFilePathAboveM(){
try {
//===========================获取UserEnvironment================
Class<?> userEnvironment = Class.forName("android.os.Environment$UserEnvironment");
Method getExternalDirs =userEnvironment.getDeclaredMethod("getExternalDirs");
getExternalDirs.setAccessible(true);
//========获取构造UserEnvironment的必要参数UserId================
Class<?> userHandle = Class.forName("android.os.UserHandle");
Method myUserId = userHandle.getDeclaredMethod("myUserId");
myUserId.setAccessible(true);
int mUserId = (int) myUserId.invoke(UserHandle.class);
Constructor<?> declaredConstructor = userEnvironment.getDeclaredConstructor(Integer.TYPE);
// 得到UserEnvironment instance
Object instance = declaredConstructor.newInstance(mUserId);
File[] files = (File[]) getExternalDirs.invoke(instance);
for (int i = 0; i < files.length; i++) {
if (Environment.isExternalStorageRemovable(files[i])){
return files[i].getPath();
}
}
} catch (Exception e) {
CrashHandler.getInstance().saveExceptionAsCrash(e);
}
return "";
}
华为Nova2S的外置SD卡的路径还有:/storage/sdcard1 , mnt/ext_sdcard
**
怎样操作外置SD卡文件
简单的FileBrowser类似ES文件管理器
具体介绍使用方法的文章
在android 4.4以上系统对于外置sd卡的写操作限制在app的包名目录下
//让系统在sd卡上建立我们的包名目录
context.getExternalFilesDir(null);
————————————————
版权声明:本文为CSDN博主「xingnan4414」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xingnan4414/article/details/79388972