DataStore
问题
DataStore 是什么?为什么要用它替代 SharedPreferences?
答案
核心概念
DataStore 是 Jetpack 推出的现代化数据存储方案,提供两种实现:
- Preferences DataStore:键值对存储(替代 SharedPreferences)
- Proto DataStore:使用 Protocol Buffers 存储自定义类型
SharedPreferences 的问题
| 问题 | SharedPreferences | DataStore |
|---|---|---|
| 阻塞主线程 | commit() 同步阻塞 | 完全异步(基于 Flow) |
| 类型安全 | 运行时 ClassCastException | 编译时检查 |
| 错误处理 | 解析失败可能异常 | 通过 Flow 的 catch |
| 数据一致性 | apply() 可能丢失 | 事务保证 |
| ANR 风险 | getString() 首次加载 | 无 |
Preferences DataStore
// 定义 DataStore
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
// 定义 Key
object PreferenceKeys {
val DARK_MODE = booleanPreferencesKey("dark_mode")
val FONT_SIZE = intPreferencesKey("font_size")
val USERNAME = stringPreferencesKey("username")
}
// 读取
val darkModeFlow: Flow<Boolean> = context.dataStore.data
.catch { exception ->
if (exception is IOException) emit(emptyPreferences())
else throw exception
}
.map { prefs ->
prefs[PreferenceKeys.DARK_MODE] ?: false
}
// 写入
suspend fun setDarkMode(enabled: Boolean) {
context.dataStore.edit { prefs ->
prefs[PreferenceKeys.DARK_MODE] = enabled
}
}
Proto DataStore
使用 Protocol Buffers 定义有类型的存储模型:
// user_settings.proto
syntax = "proto3";
message UserSettings {
bool dark_mode = 1;
int32 font_size = 2;
string language = 3;
}
// 创建序列化器
object UserSettingsSerializer : Serializer<UserSettings> {
override val defaultValue: UserSettings = UserSettings.getDefaultInstance()
override suspend fun readFrom(input: InputStream) =
UserSettings.parseFrom(input)
override suspend fun writeTo(t: UserSettings, output: OutputStream) =
t.writeTo(output)
}
// 使用
val settingsFlow: Flow<UserSettings> = protoDataStore.data
suspend fun updateLanguage(lang: String) {
protoDataStore.updateData { it.toBuilder().setLanguage(lang).build() }
}
常见面试问题
Q1: DataStore 内部是如何保证线程安全的?
答案:
DataStore 使用单线程的 SingleProcessDataStore 通过 Actor + Mutex 确保写操作串行执行。所有 edit 操作都在同一个协程中排队执行,避免竞态条件。
Q2: Preferences DataStore 和 Proto DataStore 怎么选?
答案:
- 简单配置(开关、数值)→ Preferences DataStore
- 结构化数据(多字段、嵌套、枚举)→ Proto DataStore(类型安全,不需要手动 Key 管理)
Q3: DataStore 如何从 SharedPreferences 迁移?
答案:
val dataStore = context.createDataStore(
name = "settings",
produceMigrations = { context ->
listOf(SharedPreferencesMigration(context, "old_sp_name"))
}
)
迁移完成后原 SP 文件自动删除。迁移只执行一次。