# StorageInfo 存储空间详细信息使用示例 ## 新增功能说明 `StorageInfo` 类已增强,新增以下功能: - **Apps & Data 大小**: 应用程序和用户数据占用的存储空间 - **System 大小**: 系统分区占用的存储空间 - **Free 大小**: 可用的存储空间 - **Cache 大小**: 缓存占用的存储空间 - **存储分解信息**: 详细的存储空间使用情况分析 - **存储摘要**: 格式化的完整存储信息 ## 核心数据类 ### StorageBreakdown ```kotlin 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. 获取基本存储信息 ```kotlin 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. 获取格式化的存储信息 ```kotlin 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. 获取完整的存储分解信息 ```kotlin 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. 获取存储使用百分比 ```kotlin 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. 获取完整的存储摘要 ```kotlin 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 中显示存储信息 ```kotlin @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. 存储空间监控 ```kotlin 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. 计算可安装应用数量 ```kotlin 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. 存储趋势分析 ```kotlin 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. 比较不同类别的存储占用 ```kotlin 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 级别 ## 性能优化建议 ### 异步加载 ```kotlin // 在协程中加载 lifecycleScope.launch(Dispatchers.IO) { val storageInfo = StorageInfo(context) val breakdown = storageInfo.getInternalStorageBreakdown() withContext(Dispatchers.Main) { // 更新 UI updateStorageUI(breakdown) } } ``` ### 缓存结果 ```kotlin 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. **用户提醒**: 低存储空间警告