19 KiB
19 KiB
StorageInfo 存储空间详细信息使用示例
新增功能说明
StorageInfo 类已增强,新增以下功能:
- Apps & Data 大小: 应用程序和用户数据占用的存储空间
- System 大小: 系统分区占用的存储空间
- Free 大小: 可用的存储空间
- Cache 大小: 缓存占用的存储空间
- 存储分解信息: 详细的存储空间使用情况分析
- 存储摘要: 格式化的完整存储信息
核心数据类
StorageBreakdown
data class StorageBreakdown(
val totalSpace: Long, // 总空间(字节)
val usedSpace: Long, // 已使用空间(字节)
val freeSpace: Long, // 可用空间(字节)
val systemSize: Long, // 系统占用(字节)
val appsAndDataSize: Long, // 应用和数据占用(字节)
val cacheSize: Long, // 缓存占用(字节)
val otherSize: Long // 其他占用(字节)
) {
val usedPercentage: Double // 已使用百分比
val freePercentage: Double // 可用百分比
}
使用示例
1. 获取基本存储信息
val storageInfo = StorageInfo(context)
// 直接访问属性获取大小
val appsDataSize = storageInfo.applicationsAndDataSize
println("Apps & Data: ${storageInfo.formatBytes(appsDataSize)}")
val systemSize = storageInfo.systemSize
println("System: ${storageInfo.formatBytes(systemSize)}")
val freeSpace = storageInfo.internalStorageAvailableSpace
println("Free: ${storageInfo.formatBytes(freeSpace)}")
val cacheSize = storageInfo.cacheSize
println("Cache: ${storageInfo.formatBytes(cacheSize)}")
输出示例:
Apps & Data: 18.5 GB
System: 8.2 GB
Free: 32.1 GB
Cache: 1.2 GB
2. 获取格式化的存储信息
val storageInfo = StorageInfo(context)
// 直接获取格式化的字符串
val appsData = storageInfo.getFormattedAppsAndDataSize()
val system = storageInfo.getFormattedSystemSize()
val free = storageInfo.getFormattedFreeSpace()
val cache = storageInfo.getFormattedCacheSize()
println("Apps & Data: $appsData")
println("System: $system")
println("Free: $free")
println("Cache: $cache")
3. 获取完整的存储分解信息
val storageInfo = StorageInfo(context)
// 获取详细的存储分解
val breakdown = storageInfo.getInternalStorageBreakdown()
println("=== Storage Breakdown ===")
println("Total: ${storageInfo.formatBytes(breakdown.totalSpace)}")
println("Used: ${storageInfo.formatBytes(breakdown.usedSpace)} (${String.format("%.1f%%", breakdown.usedPercentage)})")
println("Free: ${storageInfo.formatBytes(breakdown.freeSpace)} (${String.format("%.1f%%", breakdown.freePercentage)})")
println()
println("Breakdown:")
println(" System: ${storageInfo.formatBytes(breakdown.systemSize)}")
println(" Apps & Data: ${storageInfo.formatBytes(breakdown.appsAndDataSize)}")
println(" Cache: ${storageInfo.formatBytes(breakdown.cacheSize)}")
println(" Other: ${storageInfo.formatBytes(breakdown.otherSize)}")
输出示例:
=== Storage Breakdown ===
Total: 128.00 GB
Used: 85.4 GB (66.7%)
Free: 42.6 GB (33.3%)
Breakdown:
System: 8.2 GB
Apps & Data: 18.5 GB
Cache: 1.2 GB
Other: 57.5 GB
4. 获取存储使用百分比
val storageInfo = StorageInfo(context)
// 获取使用百分比
val usedPercentage = storageInfo.getUsagePercentage()
println("Storage Used: ${String.format("%.1f%%", usedPercentage)}")
// 获取可用百分比
val freePercentage = storageInfo.getFreePercentage()
println("Storage Free: ${String.format("%.1f%%", freePercentage)}")
// 判断存储状态
when {
usedPercentage > 90 -> println("Warning: Storage almost full!")
usedPercentage > 80 -> println("Storage is getting full")
usedPercentage > 50 -> println("Storage usage is moderate")
else -> println("Plenty of storage available")
}
5. 获取完整的存储摘要
val storageInfo = StorageInfo(context)
// 获取格式化的完整摘要
val summary = storageInfo.getStorageSummary()
println(summary)
输出示例:
=== Internal Storage ===
Total: 128.00 GB
Used: 85.4 GB (66.7%)
Free: 42.6 GB (33.3%)
=== Storage Breakdown ===
System: 8.2 GB
Apps & Data: 18.5 GB
Cache: 1.2 GB
Other: 57.5 GB
=== External Storage ===
Total: 256.00 GB
Used: 128.3 GB
Free: 127.7 GB
Emulated: Yes
Removable: No
=== Storage Features ===
Encrypted: Yes
Encryption Type: FBE
File System: ext4
=== Partition Scheme ===
A/B Updates: Yes
Dynamic Partitions: Yes
Virtual A/B: Yes
6. 在 Jetpack Compose 中显示存储信息
@Composable
fun StorageInfoCard() {
val context = LocalContext.current
val storageInfo = remember { StorageInfo(context) }
val breakdown = remember { storageInfo.getInternalStorageBreakdown() }
Card(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
elevation = CardDefaults.cardElevation(4.dp)
) {
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = "Storage Information",
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Bold
)
Spacer(modifier = Modifier.height(16.dp))
// 存储使用进度条
StorageProgressBar(
used = breakdown.usedSpace,
total = breakdown.totalSpace,
usedPercentage = breakdown.usedPercentage
)
Spacer(modifier = Modifier.height(16.dp))
// 总览
InfoSection(title = "Overview") {
InfoRow(
"Total",
storageInfo.formatBytes(breakdown.totalSpace)
)
InfoRow(
"Used",
"${storageInfo.formatBytes(breakdown.usedSpace)} (${String.format("%.1f%%", breakdown.usedPercentage)})"
)
InfoRow(
"Free",
"${storageInfo.formatBytes(breakdown.freeSpace)} (${String.format("%.1f%%", breakdown.freePercentage)})"
)
}
Spacer(modifier = Modifier.height(12.dp))
// 详细分解
InfoSection(title = "Breakdown") {
StorageBreakdownItem(
"System",
breakdown.systemSize,
breakdown.totalSpace,
Color(0xFF2196F3)
)
StorageBreakdownItem(
"Apps & Data",
breakdown.appsAndDataSize,
breakdown.totalSpace,
Color(0xFF4CAF50)
)
StorageBreakdownItem(
"Cache",
breakdown.cacheSize,
breakdown.totalSpace,
Color(0xFFFFC107)
)
StorageBreakdownItem(
"Other",
breakdown.otherSize,
breakdown.totalSpace,
Color(0xFF9E9E9E)
)
}
}
}
}
@Composable
fun StorageProgressBar(
used: Long,
total: Long,
usedPercentage: Double
) {
Column {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
text = "Storage Usage",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Text(
text = String.format("%.1f%%", usedPercentage),
style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.Bold
)
}
Spacer(modifier = Modifier.height(8.dp))
LinearProgressIndicator(
progress = (usedPercentage / 100).toFloat(),
modifier = Modifier
.fillMaxWidth()
.height(8.dp)
.clip(RoundedCornerShape(4.dp)),
color = when {
usedPercentage > 90 -> Color(0xFFF44336)
usedPercentage > 80 -> Color(0xFFFF9800)
else -> Color(0xFF4CAF50)
}
)
}
}
@Composable
fun StorageBreakdownItem(
label: String,
size: Long,
total: Long,
color: Color
) {
val storageInfo = StorageInfo(LocalContext.current)
val percentage = if (total > 0) (size.toDouble() / total * 100) else 0.0
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Box(
modifier = Modifier
.size(12.dp)
.background(color, CircleShape)
)
Spacer(modifier = Modifier.width(8.dp))
Text(
text = label,
style = MaterialTheme.typography.bodyMedium
)
}
Text(
text = "${storageInfo.formatBytes(size)} (${String.format("%.1f%%", percentage)})",
style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.Medium
)
}
}
@Composable
fun InfoSection(title: String, content: @Composable () -> Unit) {
Column {
Text(
text = title,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold,
color = MaterialTheme.colorScheme.primary
)
Spacer(modifier = Modifier.height(8.dp))
content()
}
}
@Composable
fun InfoRow(label: String, value: String) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
text = label,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Text(
text = value,
style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.Medium,
textAlign = TextAlign.End,
modifier = Modifier.weight(1f, fill = false)
)
}
}
7. 存储空间监控
class StorageMonitor(private val context: Context) {
private val storageInfo = StorageInfo(context)
/**
* 检查存储状态
*/
fun checkStorageStatus(): StorageStatus {
val freeSpace = storageInfo.internalStorageAvailableSpace
val usedPercentage = storageInfo.getUsagePercentage()
return when {
freeSpace < 1024 * 1024 * 1024 -> // < 1 GB
StorageStatus.CRITICAL
usedPercentage > 90 ->
StorageStatus.LOW
usedPercentage > 80 ->
StorageStatus.WARNING
else ->
StorageStatus.HEALTHY
}
}
/**
* 获取存储建议
*/
fun getStorageRecommendation(): String {
val status = checkStorageStatus()
val cacheSize = storageInfo.cacheSize
return when (status) {
StorageStatus.CRITICAL -> {
"Critical: Storage almost full! Clear ${storageInfo.formatBytes(cacheSize)} cache immediately."
}
StorageStatus.LOW -> {
"Warning: Low storage space. Consider clearing cache or removing unused apps."
}
StorageStatus.WARNING -> {
"Storage is getting full. You may want to free up some space."
}
StorageStatus.HEALTHY -> {
"Storage is healthy. ${storageInfo.getFormattedFreeSpace()} available."
}
}
}
enum class StorageStatus {
HEALTHY, WARNING, LOW, CRITICAL
}
}
// 使用示例
val monitor = StorageMonitor(context)
val status = monitor.checkStorageStatus()
val recommendation = monitor.getStorageRecommendation()
println("Storage Status: $status")
println("Recommendation: $recommendation")
8. 计算可安装应用数量
fun estimateInstallableApps(context: Context): Int {
val storageInfo = StorageInfo(context)
val freeSpace = storageInfo.internalStorageAvailableSpace
// 平均应用大小约 50 MB
val averageAppSize = 50 * 1024 * 1024L
// 保留 2 GB 作为系统缓冲
val reservedSpace = 2L * 1024 * 1024 * 1024
val availableForApps = maxOf(0L, freeSpace - reservedSpace)
return (availableForApps / averageAppSize).toInt()
}
// 使用示例
val estimatedApps = estimateInstallableApps(context)
println("You can install approximately $estimatedApps more apps")
9. 存储趋势分析
class StorageTrendAnalyzer(private val context: Context) {
private val storageInfo = StorageInfo(context)
private val prefs = context.getSharedPreferences("storage_trend", Context.MODE_PRIVATE)
/**
* 记录当前存储使用情况
*/
fun recordCurrentUsage() {
val used = storageInfo.internalStorageUsedSpace
val timestamp = System.currentTimeMillis()
prefs.edit()
.putLong("last_used", used)
.putLong("last_timestamp", timestamp)
.apply()
}
/**
* 计算每日平均增长
*/
fun getDailyGrowth(): Long {
val currentUsed = storageInfo.internalStorageUsedSpace
val lastUsed = prefs.getLong("last_used", 0)
val lastTimestamp = prefs.getLong("last_timestamp", 0)
if (lastUsed == 0L || lastTimestamp == 0L) {
return 0L
}
val daysPassed = (System.currentTimeMillis() - lastTimestamp) / (24 * 60 * 60 * 1000)
if (daysPassed == 0L) return 0L
val growth = currentUsed - lastUsed
return growth / daysPassed
}
/**
* 预估空间耗尽时间
*/
fun estimateDaysUntilFull(): Int {
val dailyGrowth = getDailyGrowth()
if (dailyGrowth <= 0) return Int.MAX_VALUE
val freeSpace = storageInfo.internalStorageAvailableSpace
val reservedSpace = 1L * 1024 * 1024 * 1024 // 保留 1 GB
val usableSpace = maxOf(0L, freeSpace - reservedSpace)
return (usableSpace / dailyGrowth).toInt()
}
}
// 使用示例
val analyzer = StorageTrendAnalyzer(context)
analyzer.recordCurrentUsage()
val dailyGrowth = analyzer.getDailyGrowth()
val daysUntilFull = analyzer.estimateDaysUntilFull()
println("Daily Growth: ${StorageInfo(context).formatBytes(dailyGrowth)}")
if (daysUntilFull < 365) {
println("Storage will be full in approximately $daysUntilFull days")
}
10. 比较不同类别的存储占用
fun compareStorageCategories(context: Context) {
val storageInfo = StorageInfo(context)
val breakdown = storageInfo.getInternalStorageBreakdown()
data class CategoryInfo(val name: String, val size: Long)
val categories = listOf(
CategoryInfo("System", breakdown.systemSize),
CategoryInfo("Apps & Data", breakdown.appsAndDataSize),
CategoryInfo("Cache", breakdown.cacheSize),
CategoryInfo("Other", breakdown.otherSize)
)
// 按大小排序
val sorted = categories.sortedByDescending { it.size }
println("=== Storage Usage by Category ===")
sorted.forEachIndexed { index, category ->
val percentage = (category.size.toDouble() / breakdown.totalSpace * 100)
val bar = "█".repeat((percentage / 2).toInt().coerceIn(0, 50))
println(
"${index + 1}. ${category.name.padEnd(15)} " +
"${storageInfo.formatBytes(category.size).padStart(10)} " +
"(${String.format("%5.1f%%", percentage)}) $bar"
)
}
// 找出占用最大的类别
val largest = sorted.first()
println("\nLargest category: ${largest.name} (${storageInfo.formatBytes(largest.size)})")
}
输出示例:
=== Storage Usage by Category ===
1. Other 57.5 GB ( 44.9%) ██████████████████████
2. Apps & Data 18.5 GB ( 14.5%) ███████
3. System 8.2 GB ( 6.4%) ███
4. Cache 1.2 GB ( 0.9%)
Largest category: Other (57.5 GB)
技术细节
计算方法
-
Apps & Data 大小:
- 扫描
/data/data(应用数据) - 扫描
/data/app(已安装应用) - 递归计算目录大小
- 扫描
-
System 大小:
- 使用
StatFs读取/system分区大小 - 包括系统应用和核心文件
- 使用
-
Cache 大小:
- 扫描
/data/cache(系统缓存) - 包括应用缓存目录
- 扫描
-
Free 大小:
- 使用
StatFs.availableBlocksLong获取可用块 - 乘以块大小得到字节数
- 使用
存储分类
- System: 操作系统和系统应用
- Apps & Data: 用户安装的应用和数据
- Cache: 临时文件和缓存
- Other: 媒体文件、下载等其他内容
注意事项
-
权限:
- 基本存储信息无需特殊权限
- 某些目录可能因权限限制无法访问
-
性能:
- 计算目录大小是耗时操作
- 建议在后台线程执行
- 考虑缓存结果
-
精确度:
- 由于权限限制,某些大小可能不完全精确
- 系统文件和隐藏文件可能未计入
-
存储单位:
- 1 KB = 1024 bytes
- 1 MB = 1024 KB
- 1 GB = 1024 MB
-
兼容性:
- 支持 Android 5.0 (API 21) 及以上
- 某些特性需要更高 API 级别
性能优化建议
异步加载
// 在协程中加载
lifecycleScope.launch(Dispatchers.IO) {
val storageInfo = StorageInfo(context)
val breakdown = storageInfo.getInternalStorageBreakdown()
withContext(Dispatchers.Main) {
// 更新 UI
updateStorageUI(breakdown)
}
}
缓存结果
class CachedStorageInfo(context: Context) {
private val storageInfo = StorageInfo(context)
private var cachedBreakdown: StorageBreakdown? = null
private var lastUpdateTime: Long = 0
fun getBreakdown(forceRefresh: Boolean = false): StorageBreakdown {
val now = System.currentTimeMillis()
val cacheExpired = now - lastUpdateTime > 5 * 60 * 1000 // 5 分钟
if (cachedBreakdown == null || cacheExpired || forceRefresh) {
cachedBreakdown = storageInfo.getInternalStorageBreakdown()
lastUpdateTime = now
}
return cachedBreakdown!!
}
}
常见应用场景
- 存储管理应用: 显示详细的存储使用情况
- 清理工具: 识别可清理的缓存和临时文件
- 应用市场: 检查是否有足够空间安装应用
- 备份工具: 评估备份所需空间
- 文件管理器: 提供存储概览
- 设置应用: 显示系统存储信息
- 性能监控: 跟踪存储趋势
- 用户提醒: 低存储空间警告