Giới thiệu
Bài viết này hướng dẫn cách lấy dung lượng bộ nhớ trống (free space) của bộ nhớ nội bộ (internal) và bộ nhớ ngoài (external/SD card) trên Android. Sử dụng StatFs (API 1+) hoặc File.getUsableSpace() (API 9+).
Các bước thực hiện
Bước 1: Tạo dự án mới
Mở Android Studio, chọn File → New Project và điền thông tin cần thiết.
Bước 2: Thiết kế giao diện (res/layout/activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:lineSpacingExtra="8dp" />
</LinearLayout>
Bước 3: Viết code lấy dung lượng (src/MainActivity.java)
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Environment;
import android.os.StatFs;
import android.widget.TextView;
import java.io.File;
public class MainActivity extends AppCompatActivity {
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textView);
displayStorageInfo();
}
private void displayStorageInfo() {
StringBuilder sb = new StringBuilder();
// 1. Internal Storage (App-specific)
File internalDir = getFilesDir();
sb.append("=== Internal Storage (App private) ===\n");
sb.append(formatStorage(internalDir));
sb.append("\n");
// 2. Internal Storage (Total - API 9+)
File internalRoot = Environment.getDataDirectory();
sb.append("=== Internal Storage (Total) ===\n");
sb.append(formatStorage(internalRoot));
sb.append("\n");
// 3. External Storage (Primary shared/storage/emulated/0)
File externalDir = Environment.getExternalStorageDirectory();
sb.append("=== External Storage (Primary) ===\n");
sb.append(formatStorage(externalDir));
sb.append("\n");
// 4. External Storage (App-specific - không cần permission)
File externalAppDir = getExternalFilesDir(null);
if (externalAppDir != null) {
sb.append("=== External Storage (App private) ===\n");
sb.append(formatStorage(externalAppDir));
}
textView.setText(sb.toString());
}
private String formatStorage(File path) {
if (path == null || !path.exists()) {
return "Path không tồn tại";
}
// Cách 1: File API (API 9+) - đơn giản, khuyên dùng
long totalBytes = path.getTotalSpace();
long freeBytes = path.getFreeSpace();
long usableBytes = path.getUsableSpace(); // Dung lượng app thực tế có thể dùng
// Cách 2: StatFs (API 1+) - chi tiết hơn, cho thiết bị cũ
// StatFs stat = new StatFs(path.getPath());
// long blockSize = stat.getBlockSizeLong();
// long totalBlocks = stat.getBlockCountLong();
// long freeBlocks = stat.getAvailableBlocksLong();
// long totalBytes = blockSize * totalBlocks;
// long freeBytes = blockSize * freeBlocks;
return String.format(
"Tổng: %s\nTrống: %s\nKha dụng: %s",
formatBytes(totalBytes),
formatBytes(freeBytes),
formatBytes(usableBytes)
);
}
private String formatBytes(long bytes) {
if (bytes < 1024) return bytes + " B";
int exp = (int) (Math.log(bytes) / Math.log(1024));
String unit = "KMGTPE".charAt(exp - 1) + "B";
return String.format("%.2f %s", bytes / Math.pow(1024, exp), unit);
}
}
Bước 4: Manifest (androidManifest.xml)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="app.com.sample">
<!-- Không cần permission READ_EXTERNAL_STORAGE cho getExternalFilesDir() -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
</intent-filter>
</activity>
</application>
</manifest>
Chạy ứng dụng
Kết nối thiết bị, nhấn Run. Giao diện hiển thị dung lượng tổng, trống, kha dụng của các phân vùng bộ nhớ.
Giải thích các khái niệm bộ nhớ
| Loại bộ nhớ | Đường dẫn típ | Quyền cần | Đặc điểm |
|---|---|---|---|
| Internal (App private) | /data/user/0/package.name/files |
Không cần | App riêng, bị xóa khi uninstall, an toàn nhất |
| Internal (System) | /data |
Root | Toàn bộ phân vùng hệ thống |
| External (Primary/Shared) | /storage/emulated/0 |
READ_EXTERNAL_STORAGE (API ≤ 32) | Chung cho user, media, download, documents |
| External (App private) | /storage/emulated/0/Android/data/pkg |
Không cần | App riêng trên external, bị xóa khi uninstall |
| Removable SD Card | /storage/xxxx-xxxx |
READ_EXTERNAL_STORAGE + SAF | Thẻ nhớ vật lý, cần Storage Access Framework |
Thay đổi theo phiên bản Android
- API 19 (KitKat): App có thể ghi vào
getExternalFilesDir()không cần permission. - API 29 (Android 10): Scoped Storage - giới hạn truy cập external shared storage. Dùng MediaStore, SAF.
- API 30 (Android 11):
READ_EXTERNAL_STORAGEkhông cho truy cập thư mục app khác. Chỉ MediaStore/SAF. - API 33 (Android 13): Granular media permissions (
READ_MEDIA_IMAGES,READ_MEDIA_VIDEO,READ_MEDIA_AUDIO).
Cách hiện đại: StorageManager (API 24+)
StorageManager storageManager = getSystemService(StorageManager.class);
StorageVolume[] volumes = storageManager.getStorageVolumes();
for (StorageVolume volume : volumes) {
String uuid = volume.getUuid();
File dir = volume.getDirectory();
boolean isPrimary = volume.isPrimary();
boolean isRemovable = volume.isRemovable();
boolean isEmulated = volume.isEmulated();
// Lấy usable space
long usableBytes = 0;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
usableBytes = volume.getUsableSpace();
}
// ...
}
Lưu ý quan trọng
getFreeSpace()vsgetUsableSpace(): Usable space tính đến giới hạn quota cho app (thường < free space).- Không hardcode đường dẫn
/sdcard- dùngEnvironment.getExternalStorageDirectory(). - Dung lượng hiển thị cho user nên dùng
Formatter.formatFileSize(context, bytes).
getExternalFilesDir(), MediaStore, Storage Access Framework.