实现原理
- 通过反射,调用StorageManger被隐藏接口getVolumePaths(),来获得手机的存储路径,进而通过StatFs获取区块blockCount与blockSize的大小,以计算出内存值
- 在使用getBlockCountLong(),getBlockSizeLong(),getAvailableBlocks()时要注意api>=18;
方法代码
/**
* 获取ROM总大小
* @param context
* @return
*/
public static String getMaxRomSpace(Context context) {
StatFs fs = new StatFs(getInnerSDCardPath(context));
String romSpace;
if (Build.VERSION.SDK_INT >=18) {
long blockCount = fs.getBlockCountLong();
long bloackSize = fs.getBlockSizeLong();
long totalSpace = bloackSize*blockCount;
romSpace = Formatter.formatFileSize(context, totalSpace);
}else{
long blockCount = fs.getBlockCount();
long bloackSize = fs.getBlockSize();
long totalSpace = bloackSize*blockCount;
romSpace = Formatter.formatFileSize(context, totalSpace);
}
return romSpace;
}
/**
* 获取SD卡总大小
* @param context
* @return
*/
public static String getMaxSDSpace(Context context) {
String extPath = getExtSDCardPath(context);
if (TextUtils.isEmpty(extPath)) {
Toast.makeText(context, "无SD卡", 0).show();
return null;
}
File path = new File(extPath);
StatFs fs = new StatFs(path.getPath());
String sdSpace;
if (Build.VERSION.SDK_INT >=18) {
long blockCount = fs.getBlockCountLong();
long bloackSize = fs.getBlockSizeLong();
long totalSpace = bloackSize*blockCount;
sdSpace = Formatter.formatFileSize(context, totalSpace);
}else{
long blockCount = fs.getBlockCount();
long bloackSize = fs.getBlockSize();
long totalSpace = bloackSize*blockCount;
sdSpace = Formatter.formatFileSize(context, totalSpace);
}
return sdSpace;
}
/**
* 获取rom可用大
* 小
* @param context
* @return
*/
public static String getAvailableRomSpace(Context context) {
File path = Environment.getDataDirectory();
StatFs fs = new StatFs(path.getPath());
String Space = null ;
if (Build.VERSION.SDK_INT >=18) {
long blockCount = fs.getAvailableBlocksLong();
long bloackSize = fs.getBlockSizeLong();
long totalSpace = bloackSize*blockCount;
Space = Formatter.formatFileSize(context, totalSpace);
}else{
long blockCount = fs.getAvailableBlocks();
long bloackSize = fs.getBlockSize();
long totalSpace = bloackSize*blockCount;
Space = Formatter.formatFileSize(context, totalSpace);
}
return Space;
}
/**
* 获取SD卡可用大小
* @param context
* @return
*/
public static String getAvailableSdSpace(Context context) {
String extPath = getExtSDCardPath(context);
if (TextUtils.isEmpty(extPath)) {
Toast.makeText(context, "无SD卡", 0).show();
return null;
}
File path = new File(extPath);
StatFs fs = new StatFs(path.getPath());
String Space;
if (Build.VERSION.SDK_INT >=18) {
long blockCount = fs.getAvailableBlocksLong();
long bloackSize = fs.getBlockSizeLong();
long totalSpace = bloackSize*blockCount;
Space = Formatter.formatFileSize(context, totalSpace);
}else{
long blockCount = fs.getAvailableBlocks();
long bloackSize = fs.getBlockSize();
long totalSpace = bloackSize*blockCount;
Space = Formatter.formatFileSize(context, totalSpace);
}
return Space;
}
/**
* 获取外置SD卡路径
* @return 应该就一条记录或空
*/
public static String getExtSDCardPath(Context context){
StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
try {
getPathsMethod = sm.getClass().getMethod("getVolumePaths", null);
String[] path = (String[]) getPathsMethod.invoke(sm, null);
return path[1];
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
/**
* 获取内置内存路径
* @return 应该就一条记录或空
*/
public static String getInnerSDCardPath(Context context){
StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
try {
getPathsMethod = sm.getClass().getMethod("getVolumePaths", null);
String[] path = (String[]) getPathsMethod.invoke(sm, null);
return path[0];
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
注意:随着Android版本的变化以及权限加强,以上方法可能不再生效。
2018/1/15更新
- 首先拿到StorageManager,然后通过它使用反射拿到getVolumeList方法,getVolumeList方法反射得到StorageVolume数组
- 遍历该StorageVolume数组,得到每一个StorageVolume,并使用反射得到其getPath方法
- getPath反射得到路径,通过StorageVolume的isRemovale方法还可以判断是否可以移除(实际上可移除的为sdcard)
/**
* 获取Android内部存储或外置SD卡路径
*
* @param context 上下文
* @param isRemovale true获取的是外部存储,false获取的是内部存储
* @return
*/
public static String getStoragePath(Context context, boolean isRemovale) {
// 拿到StorageManager
StorageManager storageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
Class<?> storageVolumeClazz = null;
try {
// 反射获得StorageVolume
storageVolumeClazz = Class.forName("android.os.storage.StorageVolume");
// 反射获得StorageManager的getVolumeList方法
Method getVolumeList = storageManager.getClass().getMethod("getVolumeList");
// 反射获得StorageVolume的getPath方法
Method getPath = storageVolumeClazz.getMethod("getPath");
// 拿到StorageVolume的isRemovable方法
Method isRemovable = storageVolumeClazz.getMethod("isRemovable");
Object result = getVolumeList.invoke(storageManager); // 得到Storage Volume数组
final int length = Array.getLength(result);
for (int i = 0; i < length; i++) { // 遍历数组,拿到StorageVolume中的路径mPath
Object storageVolume = Array.get(result, i);
String path = (String) getPath.invoke(storageVolume);
// 判断是否是可移除的,可移除表示是sdcard
boolean removable = (Boolean) isRemovable.invoke(storageVolume);
if (isRemovale == removable) { // isRemovale为true返回sdcard路径,false返回内部存储路径
return path;
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
注:以上方法通过Java反射实现的,不太了解的可以参考Java反射机制