Android 开发手册ANDROID DEV

CHAPTER 06本地存储 · PERSISTENCE

数据持久化

App 关掉再打开数据还在。从最简单的 SharedPreferences/DataStore 到对象映射 Room,再到安全存储,每种方案都有自己的主场。

开始阅读难度进阶约3小时
06.1

持久化全景

按数据量、结构复杂度与安全性选型:偏好用 DataStore,结构化关系用 Room,大文件用文件系统,密钥用加密存储。

SharedPreferences 已过时

新项目用 DataStore(基于协程/Flow、类型安全、无主线程 ANR)替代老式 SharedPreferences。

06.2

DataStore

Preferences DataStore 存简单键值,暴露 Flow;读异步、不阻塞主线程。

val Context.dataStore by preferencesDataStore("settings")
suspend fun saveDark(v: Boolean) {
    dataStore.edit { it[booleanPreferencesKey("dark")] = v }
}
val darkFlow: Flow<Boolean> = dataStore.data.map { it[booleanPreferencesKey("dark")] ?: false }
06.3

Room

Room 是 SQLite 的编译期封装:Entity + DAO + Database,SQL 在编译期校验,直接返回 Kotlin 数据类与 Flow。

@Entity data class User(@PrimaryKey val id: Long, val name: String)
@Dao interface UserDao {
    @Query("SELECT * FROM User WHERE id = :id")
    fun observe(id: Long): Flow<User>
    @Insert suspend fun insert(u: User)
}
@Database(entities = [User::class], version = 1)
abstract class AppDb : RoomDatabase() { abstract fun userDao(): UserDao }
06.4

SQLite 直连

极简或高度定制场景可用 SQLiteOpenHelper 手写 SQL,但需自行管理游标与线程。

优先 Room

直连易出错、无编译期校验。除非特殊需求,一律用 Room。

06.5

文件与媒体

内部存储 filesDir 私有无备份;外部/媒体用 MediaStore 或 Scoped Storage;缓存用 cacheDir。

val file = File(context.filesDir, "profile.json")
file.writeText(json)   // 内部存储,私有
06.6

安全存储

令牌、密钥用 EncryptedSharedPreferences 或 Android Keystore,明文绝不放普通文件。

val master = MasterKey.Builder(ctx).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build()
val enc = EncryptedSharedPreferences.create(ctx, "secret", master,
    EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
    EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM)
enc.edit().putString("token", t).apply()
06.7

选型对照

方案适合
DataStore偏好/小键值(异步、类型安全)
Room结构化关系数据
文件文档/媒体/缓存
加密存储密钥、令牌
06.8

常见陷阱

为什么 Room 主线程查询会崩溃?

默认禁止主线程访问数据库。用 suspend/Flow/RxJava 变体在后台执行,或允许主线程(不推荐)。

数据库升级怎么迁移?

用 Migration 定义版本间 SQL,或用 fallbackToDestructiveMigration(会清数据,仅开发用)。提供迁移否则用户升级崩溃。

06.9

实战练习

延伸资源