DataStore 概述与选型
DataStore 是 Android Jetpack 推出的现代化本地持久化解决方案,旨在完全替代 SharedPreferences。它基于 Kotlin 协程与 Flow 构建,提供一致且可预测的异步数据访问,并从根本上解决了 SP 的线程安全、类型不安全、同步阻塞等历史问题。
1.1 DataStore 与 SharedPreferences 对比
下表梳理了两种技术在各关键维度上的差异,以便你快速理解 DataStore 的核心改进点。
| 维度 | SharedPreferences | DataStore |
|---|---|---|
| 异步模型 | 同步阻塞 I/O(主线程风险) | 完全异步(挂起函数 + Flow) |
| 线程安全 | 不保证,容易出现并发竞态 | 基于协程,内部序列化保证安全 |
| 类型安全 | Key-Value 均以 String 存储,需手动转换 | Preferences 版同样 Key-Value 但支持基本类型;Proto 版使用 protobuf 获得强类型 |
| 异常处理 | 无内置异常策略 | 数据损坏可自定义处理,自动回滚 |
| 数据迁移 | 手动实现,极易出错 | 内置迁移 API,支持从 SP 平滑迁移 |
| 占用空间 | 单文件,高并发下性能衰退 | 差分存储,写放大低,读写性能稳定 |
核心结论:DataStore 在异步、安全、类型、维护性上全面优于 SharedPreferences。新项目应直接使用 DataStore,存量项目建议通过迁移 API 逐步替换。
1.2 DataStore 两种类型
DataStore 提供两套 API,分别对应不同的结构化复杂度需求。
- Preferences DataStore:轻量键值对存储,API 与 SP 类似但改为挂起/Flow 驱动。适合简单配置项(如开关、字符串、数字)。
- Proto DataStore:基于 protobuf 的强类型存储,数据以定义好的 schema 序列化。适合结构化数据(如用户设置、复合对象)。需定义 .proto 文件和序列化器。
添加依赖(build.gradle.kts):
// Preferences DataStore
implementation("androidx.datastore:datastore-preferences:1.1.1")
// Proto DataStore
implementation("androidx.datastore:datastore:1.1.1")
implementation("com.google.protobuf:protobuf-javalite:3.25.1")
Preferences 版示例:
// 定义 DataStore 实例
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
// 读取
val darkMode: Flow<Boolean> = dataStore.data.map { preferences ->
preferences[PreferencesKeys.DARK_MODE] ?: false
}
// 写入
suspend fun setDarkMode(enabled: Boolean) {
dataStore.edit { preferences ->
preferences[PreferencesKeys.DARK_MODE] = enabled
}
}
object PreferencesKeys {
val DARK_MODE = booleanPreferencesKey("dark_mode")
}
Proto 版示例(结合定义 proto 文件 UserSettings.proto):
syntax = "proto3";
option java_package = "com.example.datastore";
option java_multiple_files = true;
message UserSettings {
bool dark_mode = 1;
int32 notification_interval = 2;
string display_name = 3;
}
// 序列化器
object UserSettingsSerializer : Serializer<UserSettings> {
override val defaultValue = UserSettings.getDefaultInstance()
override suspend fun readFrom(input: InputStream): UserSettings {
return UserSettings.parseFrom(input)
}
override suspend fun writeTo(t: UserSettings, output: OutputStream) {
t.writeTo(output)
}
}
// 实例
val Context.userSettingsDataStore: DataStore<UserSettings> by dataStore(
fileName = "user_settings.pb",
serializer = UserSettingsSerializer
)
// 读取
val darkMode: Flow<Boolean> = userSettingsDataStore.data.map { it.darkMode }
// 写入
suspend fun setDarkMode(enabled: Boolean) {
userSettingsDataStore.updateData { settings ->
settings.toBuilder().setDarkMode(enabled).build()
}
}
选型建议:当存储项少于 10 个且均为简单类型时,首选 Preferences DataStore;当字段复杂或需要版本兼容时,Proto DataStore 更合适。
1.3 适用场景与性能优势
DataStore 在以下场景中优势明显:
- 常规应用设置(主题、语言、通知开关)
- 用户偏好持久化(账号配置、显示偏好)
- 小规模结构化数据(如历史搜索列表缓存)
- 需要实时观察数据变化的 UI 场景
性能优势:
- 异步无阻塞:所有读写均在协程中执行,不占用主线程。
- 差分写入:只将修改的部分序列化到新文件并原子替换,避免全量重写。
- Flow 响应式:数据变化自动通知所有观察者,无需手动注册监听器。
- 迁移安全:提供
SharedPreferencesMigrationAPI,可保留历史数据。
注意:DataStore 不适合大对象存储(如图片字节流)、高频率写操作(每秒数十次以上建议改用 Room)。数据损坏时默认使用 defaultValue,自定义序列化器应实现
readFrom 异常处理。