DataStore
问题
Jetpack DataStore 有哪些优势?Preferences 和 Proto 两种模式有什么区别?
答案
DataStore vs SharedPreferences
| 特性 | SharedPreferences | DataStore |
|---|---|---|
| API | 同步(可能阻塞主线程) | 基于 Flow(异步) |
| 写入方式 | 全量 XML | 事务性写入 |
| 类型安全 | ❌ | ✅(Proto 模式) |
| 错误处理 | 运行时异常 | Flow 中捕获 |
| 跨进程 | ❌(已废弃) | ❌(需 MultiProcess) |
Preferences DataStore
键值对存储,类似 SP 但基于 Flow:
// 创建 DataStore 实例
val Context.settingsStore by preferencesDataStore(name = "settings")
// 定义 Key
object PrefsKeys {
val DARK_MODE = booleanPreferencesKey("dark_mode")
val FONT_SIZE = intPreferencesKey("font_size")
val USERNAME = stringPreferencesKey("username")
}
class SettingsRepository(private val dataStore: DataStore<Preferences>) {
// 读取:返回 Flow,数据变化自动发射
val darkMode: Flow<Boolean> = dataStore.data
.catch { e ->
if (e is IOException) emit(emptyPreferences())
else throw e
}
.map { prefs -> prefs[PrefsKeys.DARK_MODE] ?: false }
// 写入:事务性操作
suspend fun setDarkMode(enabled: Boolean) {
dataStore.edit { prefs ->
prefs[PrefsKeys.DARK_MODE] = enabled
}
}
}
Proto DataStore
使用 Protocol Buffers 定义 schema,类型安全:
user_prefs.proto
syntax = "proto3";
message UserPreferences {
bool dark_mode = 1;
int32 font_size = 2;
string username = 3;
enum Theme {
SYSTEM = 0;
LIGHT = 1;
DARK = 2;
}
Theme theme = 4;
}
// Serializer
object UserPreferencesSerializer : Serializer<UserPreferences> {
override val defaultValue: UserPreferences = UserPreferences.getDefaultInstance()
override suspend fun readFrom(input: InputStream): UserPreferences =
UserPreferences.parseFrom(input)
override suspend fun writeTo(t: UserPreferences, output: OutputStream) =
t.writeTo(output)
}
// 使用
val Context.userPrefsStore by dataStore(
fileName = "user_prefs.pb",
serializer = UserPreferencesSerializer
)
class UserPrefsRepository(private val dataStore: DataStore<UserPreferences>) {
val userPrefs: Flow<UserPreferences> = dataStore.data
suspend fun updateTheme(theme: UserPreferences.Theme) {
dataStore.updateData { prefs ->
prefs.toBuilder().setTheme(theme).build()
}
}
}
从 SP 迁移到 DataStore
val Context.settingsStore by preferencesDataStore(
name = "settings",
produceMigrations = { context ->
listOf(SharedPreferencesMigration(context, "old_sp_name"))
}
)
Preferences vs Proto
- Preferences DataStore:简单键值对,无需定义 schema,适合少量配置
- Proto DataStore:类型安全,适合复杂结构化配置。需要引入 Protobuf 插件
常见面试问题
Q1: DataStore 是如何保证线程安全的?
答案:
DataStore 内部使用 SingleProcessDataStore,通过 Mutex 和 actor 模式保证同一时刻只有一个写操作。所有读写操作都在 Dispatchers.IO 上执行,通过 Flow 异步返回结果,不会阻塞主线程。
Q2: DataStore 的 edit 操作是原子性的吗?
答案:
是的。edit 操作是事务性的——先读取当前值,在 lambda 中修改,然后原子写入文件。如果 lambda 中抛出异常,修改不会被持久化。写入采用了先写临时文件再原子重命名的策略,避免写入中断导致数据损坏。