Android 获得内部/外部内存的空闲大小

我想以编程方式得到我的设备的内部/外部存储器上的空闲内存大小。我用的是这段代码:

StatFs stat = new StatFs(Environment.getExternalStorageDirectory().getPath());
long bytesAvailable = (long)stat.getBlockSize() *(long)stat.getBlockCount();
long megAvailable = bytesAvailable / 1048576;
Log.e("","Available MB : "+megAvailable);


File path = Environment.getDataDirectory();
StatFs stat2 = new StatFs(path.getPath());
long blockSize = stat2.getBlockSize();
long availableBlocks = stat2.getAvailableBlocks();
String format =  Formatter.formatFileSize(this, availableBlocks * blockSize);
Log.e("","Format : "+format);

我得到的结果是:

11-15 10:27:18.844: E/(25822): Available MB : 7572
11-15 10:27:18.844: E/(25822): Format : 869MB

问题是,我想得到的空闲内存的 SdCard,这是 1,96GB现在。我如何修复这个代码,以便我可以得到自由大小?

106881 次浏览

Below is the code for your purpose :

public static boolean externalMemoryAvailable() {
return android.os.Environment.getExternalStorageState().equals(
android.os.Environment.MEDIA_MOUNTED);
}


public static String getAvailableInternalMemorySize() {
File path = Environment.getDataDirectory();
StatFs stat = new StatFs(path.getPath());
long blockSize = stat.getBlockSizeLong();
long availableBlocks = stat.getAvailableBlocksLong();
return formatSize(availableBlocks * blockSize);
}


public static String getTotalInternalMemorySize() {
File path = Environment.getDataDirectory();
StatFs stat = new StatFs(path.getPath());
long blockSize = stat.getBlockSizeLong();
long totalBlocks = stat.getBlockCountLong();
return formatSize(totalBlocks * blockSize);
}


public static String getAvailableExternalMemorySize() {
if (externalMemoryAvailable()) {
File path = Environment.getExternalStorageDirectory();
StatFs stat = new StatFs(path.getPath());
long blockSize = stat.getBlockSizeLong();
long availableBlocks = stat.getAvailableBlocksLong();
return formatSize(availableBlocks * blockSize);
} else {
return ERROR;
}
}


public static String getTotalExternalMemorySize() {
if (externalMemoryAvailable()) {
File path = Environment.getExternalStorageDirectory();
StatFs stat = new StatFs(path.getPath());
long blockSize = stat.getBlockSizeLong();
long totalBlocks = stat.getBlockCountLong();
return formatSize(totalBlocks * blockSize);
} else {
return ERROR;
}
}


public static String formatSize(long size) {
String suffix = null;


if (size >= 1024) {
suffix = "KB";
size /= 1024;
if (size >= 1024) {
suffix = "MB";
size /= 1024;
}
}


StringBuilder resultBuffer = new StringBuilder(Long.toString(size));


int commaOffset = resultBuffer.length() - 3;
while (commaOffset > 0) {
resultBuffer.insert(commaOffset, ',');
commaOffset -= 3;
}


if (suffix != null) resultBuffer.append(suffix);
return resultBuffer.toString();
}

Get RAM Size

ActivityManager actManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
MemoryInfo memInfo = new ActivityManager.MemoryInfo();
actManager.getMemoryInfo(memInfo);
long totalMemory = memInfo.totalMem;

This is the way I did it :

StatFs stat = new StatFs(Environment.getExternalStorageDirectory().getPath());
long bytesAvailable;
if (android.os.Build.VERSION.SDK_INT >=
android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {
bytesAvailable = stat.getBlockSizeLong() * stat.getAvailableBlocksLong();
}
else {
bytesAvailable = (long)stat.getBlockSize() * (long)stat.getAvailableBlocks();
}
long megAvailable = bytesAvailable / (1024 * 1024);
Log.e("","Available MB : "+megAvailable);

@Android-Droid - you are wrong Environment.getExternalStorageDirectory() points to external storage which does not have to be SD card, it can also be mount of internal memory. See:

Find an external SD card location

Since API 9 you can do:

long freeBytesInternal = new File(ctx.getFilesDir().getAbsoluteFile().toString()).getFreeSpace();
long freeBytesExternal = new File(getExternalFilesDir(null).toString()).getFreeSpace();

To get all available storage folders (including SD cards), you first get the storage files:

File internalStorageFile=getFilesDir();
File[] externalStorageFiles=ContextCompat.getExternalFilesDirs(this,null);

Then you can get the available size of each of those.

There are 3 ways to do it:

API 8 and below:

StatFs stat=new StatFs(file.getPath());
long availableSizeInBytes=stat.getBlockSize()*stat.getAvailableBlocks();

API 9 and above:

long availableSizeInBytes=file.getFreeSpace();

API 18 and above (not needed if previous one is ok) :

long availableSizeInBytes=new StatFs(file.getPath()).getAvailableBytes();

To get a nice formatted string of what you got now, you can use:

String formattedResult=android.text.format.Formatter.formatShortFileSize(this,availableSizeInBytes);

or you can use this in case you wish to see exact bytes number but nicely:

NumberFormat.getInstance().format(availableSizeInBytes);

Do note that I think the internal storage could be the same as the first external storage, since the first one is the emulated one.


EDIT: Using StorageVolume on Android Q and above, I think it's possible to get the free space of each, using something like:

fun getStorageVolumesAccessState(context: Context) {
val storageManager = context.getSystemService(Context.STORAGE_SERVICE) as StorageManager
val storageVolumes = storageManager.storageVolumes
val storageStatsManager = context.getSystemService(Context.STORAGE_STATS_SERVICE) as StorageStatsManager
for (storageVolume in storageVolumes) {
var freeSpace: Long = 0L
var totalSpace: Long = 0L
val path = getPath(context, storageVolume)
if (storageVolume.isPrimary) {
totalSpace = storageStatsManager.getTotalBytes(StorageManager.UUID_DEFAULT)
freeSpace = storageStatsManager.getFreeBytes(StorageManager.UUID_DEFAULT)
} else if (path != null) {
val file = File(path)
freeSpace = file.freeSpace
totalSpace = file.totalSpace
}
val usedSpace = totalSpace - freeSpace
val freeSpaceStr = Formatter.formatFileSize(context, freeSpace)
val totalSpaceStr = Formatter.formatFileSize(context, totalSpace)
val usedSpaceStr = Formatter.formatFileSize(context, usedSpace)
Log.d("AppLog", "${storageVolume.getDescription(context)} - path:$path total:$totalSpaceStr used:$usedSpaceStr free:$freeSpaceStr")
}
}


fun getPath(context: Context, storageVolume: StorageVolume): String? {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
storageVolume.directory?.absolutePath?.let { return it }
try {
return storageVolume.javaClass.getMethod("getPath").invoke(storageVolume) as String
} catch (e: Exception) {
}
try {
return (storageVolume.javaClass.getMethod("getPathFile").invoke(storageVolume) as File).absolutePath
} catch (e: Exception) {
}
val extDirs = context.getExternalFilesDirs(null)
for (extDir in extDirs) {
val storageManager = context.getSystemService(Context.STORAGE_SERVICE) as StorageManager
val fileStorageVolume: StorageVolume = storageManager.getStorageVolume(extDir)
?: continue
if (fileStorageVolume == storageVolume) {
var file = extDir
while (true) {
val parent = file.parentFile ?: return file.absolutePath
val parentStorageVolume = storageManager.getStorageVolume(parent)
?: return file.absolutePath
if (parentStorageVolume != storageVolume)
return file.absolutePath
file = parent
}
}
}
try {
val parcel = Parcel.obtain()
storageVolume.writeToParcel(parcel, 0)
parcel.setDataPosition(0)
parcel.readString()
return parcel.readString()
} catch (e: Exception) {
}
return null
}

Hopefully this can help.

About external menory ,there is another way:
File external = Environment.getExternalStorageDirectory(); free:external.getFreeSpace(); total:external.getTotalSpace();

It is very easy to find out the storage available if you get internal as well as external storage path. Also phone's external storage path really very easy to find out using

Environment.getExternalStorageDirectory().getPath();

So I am just concentrating on how to find out the paths of external removable storage like removable sdcard, USB OTG(not tested USB OTG as I have no USB OTG).

Below method will give a list of all possible external removable storage paths.

 /**
* This method returns the list of removable storage and sdcard paths.
* I have no USB OTG so can not test it. Is anybody can test it, please let me know
* if working or not. Assume 0th index will be removable sdcard path if size is
* greater than 0.
* @return the list of removable storage paths.
*/
public static HashSet<String> getExternalPaths()
{
final HashSet<String> out = new HashSet<String>();
String reg = "(?i).*vold.*(vfat|ntfs|exfat|fat32|ext3|ext4).*rw.*";
String s = "";
try
{
final Process process = new ProcessBuilder().command("mount").redirectErrorStream(true).start();
process.waitFor();
final InputStream is = process.getInputStream();
final byte[] buffer = new byte[1024];
while (is.read(buffer) != -1)
{
s = s + new String(buffer);
}
is.close();
}
catch (final Exception e)
{
e.printStackTrace();
}


// parse output
final String[] lines = s.split("\n");
for (String line : lines)
{
if (!line.toLowerCase(Locale.US).contains("asec"))
{
if (line.matches(reg))
{
String[] parts = line.split(" ");
for (String part : parts)
{
if (part.startsWith("/"))
{
if (!part.toLowerCase(Locale.US).contains("vold"))
{
out.add(part.replace("/media_rw","").replace("mnt", "storage"));
}
}
}
}
}
}
//Phone's external storage path (Not removal SDCard path)
String phoneExternalPath = Environment.getExternalStorageDirectory().getPath();


//Remove it if already exist to filter all the paths of external removable storage devices
//like removable sdcard, USB OTG etc..
//When I tested it in ICE Tab(4.4.2), Swipe Tab(4.0.1) with removable sdcard, this method includes
//phone's external storage path, but when i test it in Moto X Play (6.0) with removable sdcard,
//this method does not include phone's external storage path. So I am going to remvoe the phone's
//external storage path to make behavior consistent in all the phone. Ans we already know and it easy
// to find out the phone's external storage path.
out.remove(phoneExternalPath);


return out;
}

Quick addition to External memory topic

Don't be confused by the method name externalMemoryAvailable() in Dinesh Prajapati's answer.

Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) gives you the current state of the memory, if the media is present and mounted at its mount point with read/write access. You will get true even on devices with no SD-cards, like Nexus 5. But still it's a 'must-have' method before any operations with storage.

To check if there is an SD-card on your device you can use method ContextCompat.getExternalFilesDirs()

It doesn't show transient devices, such as USB flash drives.

Also be aware that ContextCompat.getExternalFilesDirs() on Android 4.3 and lower will always return only 1 entry (SD-card if it's available, otherwise Internal). You can read more about it here.

  public static boolean isSdCardOnDevice(Context context) {
File[] storages = ContextCompat.getExternalFilesDirs(context, null);
if (storages.length > 1 && storages[0] != null && storages[1] != null)
return true;
else
return false;
}

in my case it was enough, but don't forget that some of the Android devices might have 2 SD-cards, so if you need all of them - adjust the code above.

Try this simple snippet

    public static String readableFileSize() {
long availableSpace = -1L;
StatFs stat = new StatFs(Environment.getExternalStorageDirectory().getPath());
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2)
availableSpace = (long) stat.getBlockSizeLong() * (long) stat.getAvailableBlocksLong();
else
availableSpace = (long) stat.getAvailableBlocks() * (long) stat.getBlockSize();


if(availableSpace <= 0) return "0";
final String[] units = new String[] { "B", "kB", "MB", "GB", "TB" };
int digitGroups = (int) (Math.log10(availableSpace)/Math.log10(1024));
return new DecimalFormat("#,##0.#").format(availableSpace/Math.pow(1024, digitGroups)) + " " + units[digitGroups];
}
@RequiresApi(api = Build.VERSION_CODES.O)
private void showStorageVolumes() {
StorageStatsManager storageStatsManager = (StorageStatsManager) getSystemService(Context.STORAGE_STATS_SERVICE);
StorageManager storageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
if (storageManager == null || storageStatsManager == null) {
return;
}
List<StorageVolume> storageVolumes = storageManager.getStorageVolumes();
for (StorageVolume storageVolume : storageVolumes) {
final String uuidStr = storageVolume.getUuid();
final UUID uuid = uuidStr == null ? StorageManager.UUID_DEFAULT : UUID.fromString(uuidStr);
try {
Log.d("AppLog", "storage:" + uuid + " : " + storageVolume.getDescription(this) + " : " + storageVolume.getState());
Log.d("AppLog", "getFreeBytes:" + Formatter.formatShortFileSize(this, storageStatsManager.getFreeBytes(uuid)));
Log.d("AppLog", "getTotalBytes:" + Formatter.formatShortFileSize(this, storageStatsManager.getTotalBytes(uuid)));
} catch (Exception e) {
// IGNORED
}
}
}

StorageStatsManager class introduced Android O and above which can give you free and total byte in external/internal storage. For detailed with source code, you can read my following article. you can use reflection for lower than Android O

https://medium.com/cashify-engineering/how-to-get-storage-stats-in-android-o-api-26-4b92eca6805b

This is the way i did it..

internal Total memory

double totalSize = new File(getApplicationContext().getFilesDir().getAbsoluteFile().toString()).getTotalSpace();
double totMb = totalSize / (1024 * 1024);

Internal free size

 double availableSize = new File(getApplicationContext().getFilesDir().getAbsoluteFile().toString()).getFreeSpace();
double freeMb = availableSize/ (1024 * 1024);

External free and total memory

 long freeBytesExternal =  new File(getExternalFilesDir(null).toString()).getFreeSpace();
int free = (int) (freeBytesExternal/ (1024 * 1024));
long totalSize =  new File(getExternalFilesDir(null).toString()).getTotalSpace();
int total= (int) (totalSize/ (1024 * 1024));
String availableMb = free+"Mb out of "+total+"MB";

After checking different solution write code myself this is complete code for finding

  • Total External Memory
  • Free External Memory
  • Used External Memory
  • TotaL Internal Memory
  • Used Internal Memory
  • Free Internal Memory

''''

object DeviceMemoryUtil {
private const val error: String = "Something went wrog"
private const val noExternalMemoryDetected = "No external Storage detected"
private var totalExternalMemory: Long = 0
private var freeExternalMemory: Long = 0
private var totalInternalStorage: Long = 0
private var freeInternalStorage: Long = 0


/**
* Checks weather external memory is available or not
*/
private fun externalMemoryAvailable(): Boolean {
return Environment.getExternalStorageState() ==
Environment.MEDIA_MOUNTED
}


/**
*Gives total external memory
* @return String Size of external memory
* @return Boolean True if memory size is returned
*/
fun getTotalExternalMemorySize(): Pair<String?, Boolean> {
val dirs: Array<File> = ContextCompat.getExternalFilesDirs(CanonApplication.getCanonAppInstance(), null)
return if (externalMemoryAvailable()) {
if (dirs.size > 1) {
val stat = StatFs(dirs[1].path)
val blockSize = stat.blockSizeLong
val totalBlocks = stat.blockCountLong
var totalExternalSize = totalBlocks * blockSize
totalExternalMemory = totalExternalSize
Pair(formatSize(totalExternalSize), true)
} else {
Pair(error, false)
}
} else {
Pair(noExternalMemoryDetected, false)
}
}


/**
* Gives free external memory size
* @return String Size of free external memory
* @return Boolean True if memory size is returned
*/
fun getAvailableExternalMemorySize(): Pair<String?, Boolean> {
val dirs: Array<File> = ContextCompat.getExternalFilesDirs(CanonApplication.getCanonAppInstance(), null)
if (externalMemoryAvailable()) {
return if (dirs.size > 1) {
val stat = StatFs(dirs[1].path)
val blockSize = stat.blockSizeLong
val availableBlocks = stat.availableBlocksLong
var freeExternalSize = blockSize * availableBlocks
freeExternalMemory = freeExternalSize
Pair(formatSize(freeExternalSize), true)
} else {
Pair(error, false)
}
} else {
return Pair(noExternalMemoryDetected, false)
}
}


/**
* Gives used external memory size
*  @return String Size of used external memory
* @return Boolean True if memory size is returned
*/
fun getUsedExternalMemorySize(): Pair<String?, Boolean> {
return if (externalMemoryAvailable()) {
val totalExternalSize = getTotalExternalMemorySize()
val freeExternalSize = getAvailableExternalMemorySize()
if (totalExternalSize.second && freeExternalSize.second) {
var usedExternalVolume = totalExternalMemory - freeExternalMemory
Pair(formatSize(usedExternalVolume), true)
} else {
Pair(error, false)
}
} else {
Pair(noExternalMemoryDetected, false)
}
}


/**
*Formats the long to size of memory in gb,mb etc.
* @param size Size of memory
*/
fun formatSize(size: Long): String? {
return android.text.format.Formatter.formatFileSize(CanonApplication.getCanonAppInstance(), size)
}


/**
* Gives total internal memory size
*  @return String Size of total internal memory
* @return Boolean True if memory size is returned
*/
fun getTotalInternalStorage(): Pair<String?, Boolean> {
if (showStorageVolumes()) {
return Pair(formatSize(totalInternalStorage), true)
} else {
return Pair(error, false)
}


}


/**
* Gives free or available internal memory size
*  @return String Size of free internal memory
* @return Boolean True if memory size is returned
*/
fun getFreeInternalStorageVolume(): Pair<String?, Boolean> {
return if (showStorageVolumes()) {
Pair(formatSize(freeInternalStorage), true)
} else {
Pair(error, false)
}
}


/**
*For calculation of internal storage
*/
private fun showStorageVolumes(): Boolean {
val storageManager = CanonApplication.canonApplicationInstance.applicationContext.getSystemService(Context.STORAGE_SERVICE) as StorageManager
val storageStatsManager = CanonApplication.canonApplicationInstance.applicationContext.getSystemService(Context.STORAGE_STATS_SERVICE) as StorageStatsManager
if (storageManager == null || storageStatsManager == null) {
return false
}
val storageVolumes: List<StorageVolume> = storageManager.storageVolumes
for (storageVolume in storageVolumes) {
var uuidStr: String? = null
storageVolume.uuid?.let {
uuidStr = it
}
val uuid: UUID = if (uuidStr == null) StorageManager.UUID_DEFAULT else UUID.fromString(uuidStr)
return try {
freeInternalStorage = storageStatsManager.getFreeBytes(uuid)
totalInternalStorage = storageStatsManager.getTotalBytes(uuid)
true
} catch (e: Exception) {
// IGNORED
false
}
}
return false
}


fun getTotalInternalExternalMemory(): Pair<Long?, Boolean> {
if (externalMemoryAvailable()) {
if (getTotalExternalMemorySize().second) {
if (getTotalInternalStorage().second) {
return Pair(totalExternalMemory + totalInternalStorage, true)
} else {
return Pair(0, false)
}
}
return Pair(0, false)
} else {
if (getTotalInternalStorage().second) {
return Pair(totalInternalStorage, true)
} else {
return Pair(0, false)
}
}


}


fun getTotalFreeStorage(): Pair<Long,Boolean> {
if (externalMemoryAvailable()){
if(getFreeInternalStorageVolume().second){
getFreeInternalStorageVolume()
getAvailableExternalMemorySize()
return Pair(freeExternalMemory + freeInternalStorage,true)
}
else{
return Pair(0,false)
}
}
else {
if (getFreeInternalStorageVolume().second){
getFreeInternalStorageVolume()
return Pair(freeInternalStorage,true)
}
else{
return Pair(0,false)
}
}


}}

None of the solutions mentioned here can be used for External Memory. Here is my code(for RAM, ROM, System Storage and External Storage). You can calculate free storage by using (total storage - used storage). And also, one must not use Environment.getExternalStorageDirectory() for external storage. It does not necessarily points to External SD Card. Also, this solution will work with all the Android versions (tested for API 16-30 on real devices and emulators).

    // Divide by (1024*1024*1024) to get in GB, by (1024*1024) to get in MB, by 1024 to get in KB..


// RAM
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
manager.getMemoryInfo(memoryInfo);
long totalRAM=memoryInfo.totalMem;
long availRAM=memoryInfo.availMem;  // remember to convert in GB,MB or KB.
long usedRAM=totalRAM-availRAM;


// ROM
getTotalStorageInfo(Environment.getDataDirectory().getPath());
getUsedStorageInfo(Environment.getDataDirectory().getPath());


// System Storage
getTotalStorageInfo(Environment.getRootDirectory().getPath());
getUsedStorageInfo(Environment.getRootDirectory().getPath());


// External Storage (SD Card)
File[] files = ContextCompat.getExternalFilesDirs(context, null);
if(Build.VERSION.SDK_INT<=Build.VERSION_CODES.JELLY_BEAN_MR2){
if (files.length == 1) {
Log.d("External Storage Memory","is present");
getTotalStorageInfo(files[0].getPath());
getUsedStorageInfo(files[0].getPath());
}
} else {
if (files.length > 1 && files[0] != null && files[1] != null) {
Log.d("External Storage Memory","is present");
long t=getTotalStorageInfo(files[1].getPath());
long u=getUsedStorageInfo(files[1].getPath());
System.out.println("Total External Mem: "+t+" Used External Mem: "+u+" Storage path: "+files[1].getPath());
}
}
}


public long getTotalStorageInfo(String path) {
StatFs statFs = new StatFs(path);
long t;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {
t = statFs.getTotalBytes();
} else {
t = statFs.getBlockCount() * statFs.getBlockSize();
}
return t;    // remember to convert in GB,MB or KB.
}


public long getUsedStorageInfo(String path) {
StatFs statFs = new StatFs(path);
long u;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {
u = statFs.getTotalBytes() - statFs.getAvailableBytes();
} else {
u = statFs.getBlockCount() * statFs.getBlockSize() - statFs.getAvailableBlocks() * statFs.getBlockSize();
}
return u;  // remember to convert in GB,MB or KB.
}

Now here for ROM I have used path as "/data" and for System Storage path is "/system". And for External Storage I have used ContextCompat.getExternalFilesDirs(context, null); therefore it will work on Android Q and Android R also. I hope, this will help you.