深入浅出 Android 存储(四):实战篇(上) - 应用专属存储与数据持久化

134 阅读1分钟

一、文件存储实战:私有空间的安全操作

1. 内部存储文件操作

基础API使用

// 获取文件存储目录
val internalFilesDir: File = context.filesDir // /data/data/<pkg>/files

// 写入文件
val fileName = "app_config.json"
context.openFileOutput(fileName, Context.MODE_PRIVATE).use { stream ->
    stream.write(configJson.toByteArray())
}

// 读取文件
context.openFileInput(fileName).use { stream ->
    val content = stream.bufferedReader().readText()
}

// 列出所有文件
val fileList = context.fileList() // 返回文件名数组

缓存文件管理

// 获取缓存目录
File cacheDir = context.getCacheDir(); // /data/data/<pkg>/cache

// 创建临时缓存文件
File tempFile = File.createTempFile("temp_", ".tmp", cacheDir);

// 计算缓存大小
fun calculateCacheSize(context: Context): Long {
    return context.cacheDir.walkTopDown().fold(0L) { acc, file ->
        acc + file.length()
    }
}

// 清理缓存
fun clearCache(context: Context) {
    context.cacheDir.deleteRecursively()
    context.cacheDir.mkdirs()
}

2. 应用专属外部存储操作

deepseek_mermaid_20250630_abc0a5.png

代码实现

// 获取图片存储目录
val pictureDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)

// 创建媒体目录(Android 11+推荐)
val mediaDirs = context.getExternalMediaDirs()
val mediaDir = mediaDirs.firstOrNull()?.apply { mkdirs() }

// 保存图片到外部存储
fun saveImageToExternal(bitmap: Bitmap, fileName: String) {
    val imagesDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
    val imageFile = File(imagesDir, fileName)
    
    FileOutputStream(imageFile).use { out ->
        bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out)
    }
    
    // 通知媒体扫描器(可选)
    MediaScannerConnection.scanFile(context, arrayOf(imageFile.path), null, null)
}

目录结构示例

/storage/emulated/0/
├── Android/
│   ├── data/
│   │   └── com.example.app/
│   │       ├── cache/        // 外部缓存
│   │       ├── files/        // 普通文件
│   │       │   ├── Documents/
│   │       │   ├── Pictures/  // 应用专属图片
│   │       │   └── Audio/
│   │       └── ...          
│   └── media/
│       └── com.example.app/   // 推荐媒体存储位置
│           ├── Images/        // 用户可能保留的图片
│           └── Videos/        // 用户可能保留的视频

3. 文件操作通用实践

// 1. 使用协程避免主线程阻塞
viewModelScope.launch(Dispatchers.IO) {
    saveLargeFile(data)
}

// 2. 确保流正确关闭
FileInputStream(file).use { stream ->
    // 操作流
}

// 3. 使用缓冲提升性能
BufferedInputStream(FileInputStream(file)).use { bis ->
    // 读取操作
}

// 4. 文件操作异常处理
try {
    // 文件操作
} catch (e: IOException) {
    when {
        e is FileNotFoundException -> handleFileNotFound()
        e.message?.contains("ENOSPC") == true -> showStorageFullError()
        else -> handleGenericError(e)
    }
}