跳到主要内容

DataStore

问题

DataStore 是什么?为什么要用它替代 SharedPreferences?

答案

核心概念

DataStore 是 Jetpack 推出的现代化数据存储方案,提供两种实现:

  • Preferences DataStore:键值对存储(替代 SharedPreferences)
  • Proto DataStore:使用 Protocol Buffers 存储自定义类型

SharedPreferences 的问题

问题SharedPreferencesDataStore
阻塞主线程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 文件自动删除。迁移只执行一次。

相关链接