DevCheck-lib/StorageInfo使用示例.md
2025-12-29 11:39:18 +08:00

19 KiB
Raw Blame History

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)

技术细节

计算方法

  1. Apps & Data 大小

    • 扫描 /data/data (应用数据)
    • 扫描 /data/app (已安装应用)
    • 递归计算目录大小
  2. System 大小

    • 使用 StatFs 读取 /system 分区大小
    • 包括系统应用和核心文件
  3. Cache 大小

    • 扫描 /data/cache (系统缓存)
    • 包括应用缓存目录
  4. Free 大小

    • 使用 StatFs.availableBlocksLong 获取可用块
    • 乘以块大小得到字节数

存储分类

  • System: 操作系统和系统应用
  • Apps & Data: 用户安装的应用和数据
  • Cache: 临时文件和缓存
  • Other: 媒体文件、下载等其他内容

注意事项

  1. 权限

    • 基本存储信息无需特殊权限
    • 某些目录可能因权限限制无法访问
  2. 性能

    • 计算目录大小是耗时操作
    • 建议在后台线程执行
    • 考虑缓存结果
  3. 精确度

    • 由于权限限制,某些大小可能不完全精确
    • 系统文件和隐藏文件可能未计入
  4. 存储单位

    • 1 KB = 1024 bytes
    • 1 MB = 1024 KB
    • 1 GB = 1024 MB
  5. 兼容性

    • 支持 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!!
    }
}

常见应用场景

  1. 存储管理应用: 显示详细的存储使用情况
  2. 清理工具: 识别可清理的缓存和临时文件
  3. 应用市场: 检查是否有足够空间安装应用
  4. 备份工具: 评估备份所需空间
  5. 文件管理器: 提供存储概览
  6. 设置应用: 显示系统存储信息
  7. 性能监控: 跟踪存储趋势
  8. 用户提醒: 低存储空间警告