目录
- 一、概述
- 二、添加依赖
- [三、Preferences DataStore 详细使用](#三、Preferences DataStore 详细使用 "#%E4%B8%89preferences-datastore-%E8%AF%A6%E7%BB%86%E4%BD%BF%E7%94%A8")
- [四、Proto DataStore 详细使用](#四、Proto DataStore 详细使用 "#%E5%9B%9Bproto-datastore-%E8%AF%A6%E7%BB%86%E4%BD%BF%E7%94%A8")
- 五、完整封装工具类
- 六、核心原理详解
- 七、数据完整性保证机制
- 八、多进程支持
- 九、性能特点
- 十、最佳实践
- 十一、迁移指南
- 十二、对比总结
- 十三、常见问题
一、概述
1.1 什么是 DataStore
DataStore 是 Jetpack 提供的异步、一致的数据存储解决方案,用于替代 SharedPreferences。
1.2 核心特点
| 特性 | 说明 |
|---|---|
| 完全异步 | 基于 Kotlin 协程和 Flow,不会阻塞 UI 线程 |
| 数据一致性 | 事务性写入,保证数据完整性 |
| 类型安全 | Proto DataStore 提供编译时类型检查 |
| 响应式更新 | 基于 Flow,数据变化自动通知 |
| 损坏恢复 | 内置 CorruptionHandler 机制 |
| 多进程支持 | 支持 MultiProcessDataStore |
| 迁移支持 | 支持从 SharedPreferences 平滑迁移 |
1.3 两种类型对比
| 类型 | 特点 | 适用场景 |
|---|---|---|
| Preferences DataStore | 键值对存储,类似 SharedPreferences | 简单配置数据 |
| Proto DataStore | 类型安全,使用 Protocol Buffers | 复杂数据结构,需要类型安全 |
1.4 存储位置
bash
/data/data/包名/files/datastore/xxx.preferences_pb
二、添加依赖
2.1 build.gradle 配置
groovy
dependencies {
// Preferences DataStore (推荐,简单易用)
implementation "androidx.datastore:datastore-preferences:1.0.0"
// Proto DataStore (可选,类型安全)
implementation "androidx.datastore:datastore:1.0.0"
}
// 如果使用 Proto DataStore,还需要添加插件
plugins {
id "com.google.protobuf" version "0.9.0"
}
三、Preferences DataStore 详细使用
3.1 创建 DataStore 实例
方式一:扩展属性(推荐)
kotlin
// 在顶层文件或单独的文件中定义
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(
name = "settings", // 文件名
corruptionHandler = ReplaceFileCorruptionHandler { // 损坏处理器
emptyPreferences()
},
scope = CoroutineScope(Dispatchers.IO + SupervisorJob()), // 协程作用域
produceMigrations = { context -> // 迁移配置
listOf(
SharedPreferencesMigration(context, "old_prefs_name")
)
}
)
方式二:单例模式
kotlin
object DataStoreManager {
private var INSTANCE: DataStore<Preferences>? = null
fun getInstance(context: Context): DataStore<Preferences> {
return INSTANCE ?: synchronized(this) {
INSTANCE ?: PreferenceDataStoreFactory.create(
corruptionHandler = ReplaceFileCorruptionHandler { emptyPreferences() },
migrations = listOf(SharedPreferencesMigration(context, "old_prefs")),
scope = CoroutineScope(Dispatchers.IO + SupervisorJob()),
file = context.preferencesDataStoreFile("settings")
).also { INSTANCE = it }
}
}
}
3.2 定义 Key
kotlin
/**
* Preferences Key 定义
*
* 支持的类型:String, Int, Long, Float, Double, Boolean, Set<String>
*/
object PreferenceKeys {
val USER_NAME = stringPreferencesKey("user_name")
val USER_AGE = intPreferencesKey("user_age")
val USER_SCORE = floatPreferencesKey("user_score")
val IS_LOGIN = booleanPreferencesKey("is_login")
val TIMESTAMP = longPreferencesKey("timestamp")
val BALANCE = doublePreferencesKey("balance")
val TAGS = stringSetPreferencesKey("tags")
}
3.3 写入数据
kotlin
// 写入单个数据
suspend fun saveUserName(context: Context, name: String) {
context.dataStore.edit { preferences ->
preferences[PreferenceKeys.USER_NAME] = name
}
}
// 批量写入数据
suspend fun saveUserInfo(context: Context, name: String, age: Int, isLogin: Boolean) {
context.dataStore.edit { preferences ->
preferences[PreferenceKeys.USER_NAME] = name
preferences[PreferenceKeys.USER_AGE] = age
preferences[PreferenceKeys.IS_LOGIN] = isLogin
}
}
// 使用 updateData(返回新数据)
suspend fun incrementLoginCount(context: Context): Int {
return context.dataStore.updateData { preferences ->
val currentCount = preferences[PreferenceKeys.LOGIN_COUNT] ?: 0
preferences.toMutablePreferences().apply {
this[PreferenceKeys.LOGIN_COUNT] = currentCount + 1
}
}[PreferenceKeys.LOGIN_COUNT] ?: 0
}
3.4 读取数据
方式一:Flow 响应式读取(推荐)
kotlin
// 单个值
val userNameFlow: Flow<String> = context.dataStore.data
.map { preferences ->
preferences[PreferenceKeys.USER_NAME] ?: ""
}
// 组合多个值
data class UserInfo(
val name: String,
val age: Int,
val isLogin: Boolean
)
val userInfoFlow: Flow<UserInfo> = context.dataStore.data
.map { preferences ->
UserInfo(
name = preferences[PreferenceKeys.USER_NAME] ?: "",
age = preferences[PreferenceKeys.USER_AGE] ?: 0,
isLogin = preferences[PreferenceKeys.IS_LOGIN] ?: false
)
}
方式二:一次性读取
kotlin
suspend fun getUserName(context: Context): String {
return context.dataStore.data.first()[PreferenceKeys.USER_NAME] ?: ""
}
方式三:带异常处理的读取
kotlin
val safeUserNameFlow: Flow<String> = context.dataStore.data
.catch { exception ->
if (exception is IOException) {
emit(emptyPreferences())
} else {
throw exception
}
}
.map { preferences ->
preferences[PreferenceKeys.USER_NAME] ?: ""
}
3.5 删除数据
kotlin
// 删除单个 key
suspend fun removeUserName(context: Context) {
context.dataStore.edit { preferences ->
preferences.remove(PreferenceKeys.USER_NAME)
}
}
// 清空所有数据
suspend fun clearAll(context: Context) {
context.dataStore.edit { preferences ->
preferences.clear()
}
}
3.6 监听数据变化
kotlin
context.dataStore.data
.map { it[PreferenceKeys.USER_NAME] ?: "" }
.distinctUntilChanged() // 只有值真正变化时才通知
.collect { name ->
Log.d("DataStore", "Name changed to: $name")
updateUI(name)
}
四、Proto DataStore 详细使用
4.1 定义 Proto 文件
protobuf
// app/src/main/proto/user.proto
syntax = "proto3";
option java_package = "com.example.app.data";
option java_multiple_files = true;
message User {
string name = 1;
int32 age = 2;
bool is_login = 3;
float score = 4;
int64 timestamp = 5;
message Address {
string city = 1;
string street = 2;
}
Address address = 6;
repeated string tags = 7; // 列表类型
}
4.2 创建 Serializer
kotlin
object UserSerializer : Serializer<User> {
// 默认值,文件不存在或损坏时使用
override val defaultValue: User = User.getDefaultInstance()
.toBuilder()
.setName("")
.setAge(0)
.setIsLogin(false)
.build()
// 从输入流读取
override suspend fun readFrom(input: InputStream): User {
return try {
User.parseFrom(input)
} catch (e: InvalidProtocolBufferException) {
defaultValue
}
}
// 写入到输出流
override suspend fun writeTo(t: User, output: OutputStream) {
t.writeTo(output)
}
}
4.3 创建 Proto DataStore
kotlin
val Context.userStore: DataStore<User> by dataStore(
fileName = "user.pb",
serializer = UserSerializer
)
4.4 读写数据
kotlin
// 读取数据
val userNameFlow: Flow<String> = context.userStore.data
.map { user -> user.name }
// 写入数据
suspend fun updateUserName(context: Context, name: String) {
context.userStore.updateData { user ->
user.toBuilder()
.setName(name)
.build()
}
}
// 更新嵌套对象
suspend fun updateUserAddress(context: Context, city: String, street: String) {
context.userStore.updateData { user ->
val address = User.Address.newBuilder()
.setCity(city)
.setStreet(street)
.build()
user.toBuilder()
.setAddress(address)
.build()
}
}
五、完整封装工具类
kotlin
/**
* DataStore 工具类
*/
object DataStoreHelper {
private const val TAG = "DataStoreHelper"
private const val DEFAULT_FILE_NAME = "app_config"
private var dataStore: DataStore<Preferences>? = null
/**
* 初始化(在 Application 中调用)
*/
fun init(context: Context) {
if (dataStore == null) {
dataStore = PreferenceDataStoreFactory.create(
corruptionHandler = ReplaceFileCorruptionHandler { emptyPreferences() },
migrations = listOf(SharedPreferencesMigration(context, "old_prefs")),
scope = CoroutineScope(Dispatchers.IO + SupervisorJob()),
file = context.preferencesDataStoreFile(DEFAULT_FILE_NAME)
)
}
}
private fun getDataStore(): DataStore<Preferences> {
return dataStore
?: throw IllegalStateException("DataStore not initialized. Call init() first.")
}
// ========== 写入操作 ==========
suspend fun putString(key: String, value: String) {
getDataStore().edit { it[stringPreferencesKey(key)] = value }
}
suspend fun putInt(key: String, value: Int) {
getDataStore().edit { it[intPreferencesKey(key)] = value }
}
suspend fun putLong(key: String, value: Long) {
getDataStore().edit { it[longPreferencesKey(key)] = value }
}
suspend fun putFloat(key: String, value: Float) {
getDataStore().edit { it[floatPreferencesKey(key)] = value }
}
suspend fun putBoolean(key: String, value: Boolean) {
getDataStore().edit { it[booleanPreferencesKey(key)] = value }
}
// ========== 批量写入 ==========
suspend fun putAll(vararg pairs: Pair<String, Any>) {
getDataStore().edit { prefs ->
pairs.forEach { (key, value) ->
when (value) {
is String -> prefs[stringPreferencesKey(key)] = value
is Int -> prefs[intPreferencesKey(key)] = value
is Long -> prefs[longPreferencesKey(key)] = value
is Float -> prefs[floatPreferencesKey(key)] = value
is Boolean -> prefs[booleanPreferencesKey(key)] = value
}
}
}
}
// ========== 读取操作(Flow)==========
fun getStringFlow(key: String, defaultValue: String = ""): Flow<String> {
return getDataStore().data
.catch { e ->
if (e is IOException) emit(emptyPreferences())
}
.map { it[stringPreferencesKey(key)] ?: defaultValue }
.distinctUntilChanged()
}
fun getIntFlow(key: String, defaultValue: Int = 0): Flow<Int> {
return getDataStore().data
.catch { e ->
if (e is IOException) emit(emptyPreferences())
}
.map { it[intPreferencesKey(key)] ?: defaultValue }
.distinctUntilChanged()
}
fun getBooleanFlow(key: String, defaultValue: Boolean = false): Flow<Boolean> {
return getDataStore().data
.catch { e ->
if (e is IOException) emit(emptyPreferences())
}
.map { it[booleanPreferencesKey(key)] ?: defaultValue }
.distinctUntilChanged()
}
// ========== 读取操作(一次性)==========
suspend fun getString(key: String, defaultValue: String = ""): String {
return getDataStore().data.first()[stringPreferencesKey(key)] ?: defaultValue
}
suspend fun getInt(key: String, defaultValue: Int = 0): Int {
return getDataStore().data.first()[intPreferencesKey(key)] ?: defaultValue
}
suspend fun getBoolean(key: String, defaultValue: Boolean = false): Boolean {
return getDataStore().data.first()[booleanPreferencesKey(key)] ?: defaultValue
}
// ========== 删除操作 ==========
suspend fun remove(key: String) {
getDataStore().edit { it.remove(stringPreferencesKey(key)) }
}
suspend fun clear() {
getDataStore().edit { it.clear() }
}
// ========== 检查操作 ==========
suspend fun contains(key: String): Boolean {
return getDataStore().data.first().contains(stringPreferencesKey(key))
}
}
使用示例
kotlin
// 在 Application 中初始化
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
DataStoreHelper.init(this)
}
}
// 在 Activity/ViewModel 中使用
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 写入数据
lifecycleScope.launch {
DataStoreHelper.putString("user_name", "张三")
DataStoreHelper.putInt("user_age", 25)
}
// 响应式读取
lifecycleScope.launch {
DataStoreHelper.getStringFlow("user_name")
.collect { name -> textView.text = name }
}
// 一次性读取
lifecycleScope.launch {
val name = DataStoreHelper.getString("user_name")
updateUI(name)
}
}
}
六、核心原理详解
6.1 数据加载流程
css
首次访问 data 属性:
┌──────────────────────────────────────────────────────────────────┐
│ 1. 创建 Flow │
│ ↓ │
│ 2. 检查缓存 │
│ ├── 有缓存 → 直接返回 Flow │
│ └── 无缓存 → 继续加载 │
│ ↓ │
│ 3. 执行迁移(如果配置了) │
│ SharedPreferencesMigration: │
│ - 读取旧 SP 数据 │
│ - 合并到 DataStore │
│ - 删除旧 SP 文件 │
│ ↓ │
│ 4. 读取文件 │
│ ├── 文件存在 → 解析 ProtoBuf │
│ ├── 文件不存在 → 返回 defaultValue │
│ └── 文件损坏 → 触发 CorruptionHandler │
│ ↓ │
│ 5. 更新缓存并发送给订阅者 │
└──────────────────────────────────────────────────────────────────┘
6.2 数据写入流程
css
edit {} 或 updateData {} 调用:
┌──────────────────────────────────────────────────────────────────┐
│ 1. 获取当前数据(等待进行中的写入完成) │
│ ↓ │
│ 2. 执行转换(调用传入的 lambda) │
│ ↓ │
│ 3. 更新内存缓存(立即生效) │
│ ↓ │
│ 4. 发送新数据给订阅者 │
│ ↓ │
│ 5. 写入磁盘 │
│ ├── a. 创建临时文件: file.tmp │
│ ├── b. 序列化数据写入临时文件 │
│ ├── c. 调用 FileChannel.force(true) 刷新到磁盘 │
│ ├── d. 原子重命名: file.tmp → file │
│ └── e. 删除备份文件 │
│ ↓ │
│ 6. 完成 │
└──────────────────────────────────────────────────────────────────┘
七、数据完整性保证机制
7.1 原子写入机制
ini
为什么需要原子写入?
┌──────────────────────────────────────────────────────────────────┐
│ 直接写入的问题: │
│ 原文件: [完整数据] │
│ ↓ 直接写入 │
│ 写入中: [一半新数据][一半旧数据] ← 崩溃! │
│ ↓ │
│ 结果: [损坏的数据] 无法恢复 │
└──────────────────────────────────────────────────────────────────┘
DataStore 的原子写入:
┌──────────────────────────────────────────────────────────────────┐
│ 步骤 1:写入临时文件 │
│ 原文件: [完整数据] │
│ 临时文件: [新数据] (独立文件) │
│ │
│ 步骤 2:原子重命名 │
│ file.renameTo(temp) 是原子操作: │
│ • 要么成功:临时文件完全替换原文件 │
│ • 要么失败:原文件保持不变 │
│ • 不会出现"半替换"状态 │
│ │
│ 步骤 3:崩溃恢复 │
│ • 写入前崩溃:原文件完整,临时文件不存在 │
│ • 写入后崩溃:原文件完整,临时文件被忽略 │
│ • 重命名后崩溃:新文件完整 │
└──────────────────────────────────────────────────────────────────┘
7.2 fsync 确保持久化
kotlin
// DataStore 在写入时会调用
stream.channel.force(true) // 等同于 fsync
// 效果:强制将数据从操作系统缓存刷入磁盘
// 代价:写入变慢,但数据安全
7.3 CorruptionHandler 损坏恢复
kotlin
// 使用内置实现
val Context.dataStore by preferencesDataStore(
name = "settings",
corruptionHandler = ReplaceFileCorruptionHandler {
// 损坏时返回默认数据
emptyPreferences()
}
)
// 自定义损坏处理器
class CustomCorruptionHandler : CorruptionHandler<Preferences> {
override suspend fun handleCorruption(ex: Exception): Preferences {
Log.w("DataStore", "Data corruption detected", ex)
// 1. 上报错误
// 2. 尝试从备份恢复
// 3. 返回默认值
return emptyPreferences()
}
}
八、多进程支持
8.1 创建 MultiProcessDataStore
kotlin
val Context.multiProcessDataStore: DataStore<Preferences> by multiProcessDataStore(
fileName = "shared_config.preferences_pb",
corruptionHandler = ReplaceFileCorruptionHandler { emptyPreferences() }
)
8.2 多进程同步原理
scss
核心机制:
┌──────────────────────────────────────────────────────────────────┐
│ 1. 文件锁 (FileLock) │
│ • 跨进程互斥 │
│ • 保证写入原子性 │
│ │
│ 2. 内容观察者 (ContentObserver) │
│ • 监听文件变化 │
│ • 其他进程修改时收到通知 │
│ │
│ 3. 主动轮询(兜底) │
│ • 定期检查文件修改时间 │
│ • 防止通知丢失 │
└──────────────────────────────────────────────────────────────────┘
同步流程:
进程 A (写入) 进程 B (读取)
┌─────────────────┐ ┌─────────────────┐
│ 1. 获取文件锁 │ │ │
│ 2. 写入数据 │ │ │
│ 3. 释放锁 │───────────>│ ContentObserver │
│ 4. 通知变化 │ │ 被触发 │
└─────────────────┘ │ 重新加载数据 │
│ 更新 Flow │
└─────────────────┘
8.3 多进程注意事项
kotlin
// 1. 使用正确的工厂方法
// ✗ 错误:单进程 DataStore
val singleProcessDataStore = context.dataStore
// ✓ 正确:多进程 DataStore
val multiProcessDataStore = MultiProcessDataStoreFactory.create(
file = context.preferencesDataStoreFile("shared"),
corruptionHandler = ReplaceFileCorruptionHandler { emptyPreferences() }
)
// 2. 避免竞态条件 - 使用 updateData
suspend fun incrementCounter(dataStore: DataStore<Preferences>) {
// ✓ 正确:updateData 是原子操作
dataStore.updateData { prefs ->
prefs.toMutablePreferences().apply {
val current = this[COUNTER] ?: 0
this[COUNTER] = current + 1
}
}
}
九、性能特点
9.1 内存使用
| 场景 | 建议 |
|---|---|
| < 100KB | ✅ 推荐 |
| 100KB - 1MB | ⚠️ 可接受 |
| > 1MB | ❌ 不推荐(使用数据库/文件存储) |
9.2 读写性能
scss
读取性能:
• 首次读取:需要解析 ProtoBuf 文件,较慢
• 后续读取:直接从内存读取,极快
• Flow 订阅:数据变化时自动通知,无额外开销
写入性能:
• 内存更新:同步,立即生效
• 磁盘写入:异步,在 IO 线程
• fsync 开销:每次写入都会 fsync,保证数据安全
优化建议:
• 批量写入,减少 edit() 调用次数
• 不要在循环中频繁写入
• 使用 distinctUntilChanged() 减少重复通知
十、最佳实践
10.1 项目结构
bash
app/
├── src/main/java/com/example/app/
│ ├── data/
│ │ ├── datastore/
│ │ │ ├── DataStoreHelper.kt # 工具类
│ │ │ └── PreferenceKeys.kt # Key 定义
│ │ └── proto/
│ │ └── user.proto # Proto 定义
│ └── ...
10.2 Key 管理
kotlin
object PreferenceKeys {
// 用户相关
object User {
val NAME = stringPreferencesKey("user_name")
val AGE = intPreferencesKey("user_age")
val IS_LOGIN = booleanPreferencesKey("user_is_login")
}
// 设置相关
object Settings {
val DARK_MODE = booleanPreferencesKey("settings_dark_mode")
val LANGUAGE = stringPreferencesKey("settings_language")
}
}
10.3 响应式 UI 集成
kotlin
// ViewModel 中使用
class UserViewModel(
private val dataStore: DataStore<Preferences>
) : ViewModel() {
data class UserUiState(
val name: String = "",
val age: Int = 0,
val isLogin: Boolean = false
)
val uiState: StateFlow<UserUiState> = dataStore.data
.map { prefs ->
UserUiState(
name = prefs[PreferenceKeys.User.NAME] ?: "",
age = prefs[PreferenceKeys.User.AGE] ?: 0,
isLogin = prefs[PreferenceKeys.User.IS_LOGIN] ?: false
)
}
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), UserUiState())
fun updateName(name: String) {
viewModelScope.launch {
dataStore.edit { it[PreferenceKeys.User.NAME] = name }
}
}
}
// Compose 中使用
@Composable
fun UserScreen(viewModel: UserViewModel) {
val uiState by viewModel.uiState.collectAsState()
Column {
Text(text = "Name: ${uiState.name}")
Button(onClick = { viewModel.updateName("新名字") }) {
Text("Update")
}
}
}
10.4 异常处理
kotlin
// 安全读取
val safeFlow = dataStore.data
.catch { e ->
Log.e(TAG, "Read failed", e)
if (e is IOException) {
emit(emptyPreferences())
} else {
throw e
}
}
.map { prefs -> prefs[KEY] ?: defaultValue }
// 安全写入
suspend fun safeWrite(block: Preferences.MutablePreferences.() -> Unit): Boolean {
return try {
dataStore.edit(block)
true
} catch (e: Exception) {
Log.e(TAG, "Write failed", e)
false
}
}
十一、迁移指南
11.1 从 SharedPreferences 迁移
kotlin
// 方式一:自动迁移(推荐)
val Context.dataStore by preferencesDataStore(
name = "settings",
produceMigrations = { context ->
listOf(
SharedPreferencesMigration(
context = context,
sharedPreferencesName = "old_prefs",
keysToMigrate = setOf("name", "age", "is_login") // 可选:指定迁移的 key
)
)
}
)
// 方式二:手动迁移
suspend fun migrateFromSharedPreferences(
context: Context,
oldPrefsName: String,
dataStore: DataStore<Preferences>
) {
val oldPrefs = context.getSharedPreferences(oldPrefsName, Context.MODE_PRIVATE)
// 读取旧数据
val name = oldPrefs.getString("name", "") ?: ""
val age = oldPrefs.getInt("age", 0)
// 写入新数据
dataStore.edit { prefs ->
prefs[stringPreferencesKey("name")] = name
prefs[intPreferencesKey("age")] = age
}
// 清理旧数据
oldPrefs.edit().clear().apply()
}
十二、对比总结
12.1 DataStore vs SharedPreferences vs MMKV
| 特性 | SharedPreferences | DataStore | MMKV |
|---|---|---|---|
| API 风格 | 同步 | 异步 (Flow + 协程) | 同步 + 回调 |
| 类型安全 | ✗ | ✓ | △ |
| 数据完整性 | △ (可能丢失) | ✓ (原子写入) | ✓ (mmap) |
| 多进程支持 | ✗ (已废弃) | ✓ | ✓ (原生) |
| ANR 风险 | ✓ (有) | △ (低) | ✗ (无) |
| 性能 | 中 | 中 | 极高 |
| Jetpack 集成 | ✗ | ✓ | ✗ |
| 官方支持 | ✓ | ✓ | ✗ (腾讯) |
12.2 选型建议
| 场景 | 推荐 |
|---|---|
| 新项目 | DataStore (Preferences 或 Proto) |
| 高性能需求 | MMKV |
| 多进程高频访问 | MMKV |
| 旧项目维护 | SharedPreferences |
| 需要 Jetpack 集成 | DataStore |
scss
─────────────────────────────────────────────────────────────────────────────────┐
│ 核心特性对比 │
├─────────────────────────────────────────────────────────────────────────────────┤
│ │
│ 特性 │ SP │ DataStore │ MMKV │
│ ──────────────────┼───────────────┼────────────────┼────────────────────────│
│ 发布者 │ Google │ Google/Jetpack │ 腾讯 │
│ ──────────────────┼───────────────┼────────────────┼────────────────────────│
│ 存储格式 │ XML │ ProtoBuf │ 内存映射 + 二进制 │
│ ──────────────────┼───────────────┼────────────────┼────────────────────────│
│ 数据结构 │ 键值对 │ 键值对/Proto │ 键值对 │
│ ──────────────────┼───────────────┼────────────────┼────────────────────────│
│ API 风格 │ 同步 │ 异步+ 协程│ 同步 + 异步回调 │
│ ──────────────────┼───────────────┼────────────────┼────────────────────────│
│ 类型安全 │ ✗ │ ✓ │ △ (部分) │
│ ──────────────────┼───────────────┼────────────────┼────────────────────────│
│ 数据完整性 │ △ (可能丢失) │ ✓ (原子写入) │ ✓ (mmap 特性) │
│ ──────────────────┼───────────────┼────────────────┼────────────────────────│
│ 多进程支持 │ ✗ (已废弃) │ ✓ (MultiProcess)│ ✓ (原生支持) │
│ ──────────────────┼───────────────┼────────────────┼────────────────────────│
│ ANR 风险 │ ✓ (有) │ △ (apply 可能) │ ✗ (无) │
│ ──────────────────┼───────────────┼────────────────┼────────────────────────│
│ 跨平台 │ ✗ │ ✗ │ ✓ (Android/iOS/等) │
│ ──────────────────┼───────────────┼────────────────┼────────────────────────│
│ 加密支持 │ ✗ │ ✗ │ ✓ (CRC/加密) │
│ ──────────────────┼───────────────┼────────────────┼────────────────────────│
│ 包体积增量 │ 0 │ ~50KB │ ~500KB │
│ ──────────────────┼───────────────┼────────────────┼────────────────────────│
│ 读取性能 │ 中 │ 中 │ 极高 │
│ ──────────────────┼───────────────┼────────────────┼────────────────────────│
│ 写入性能 │ 慢 │ 中 │ 极高 │
│ │
└─────────────────────────────────────────────────────────────────────────────────
十三、常见问题
Q1: DataStore 和 SharedPreferences 可以同时使用吗?
不建议。如果使用 DataStore,应该迁移所有 SharedPreferences 数据,避免数据不一致。
Q2: 如何处理大量数据?
DataStore 不适合存储大量数据(> 1MB)。建议使用:
- Room 数据库(结构化数据)
- 文件存储(大文件)
- MMKV(高性能键值对)
Q3: apply() 会阻塞吗?
DataStore 没有 apply/commit 的概念。所有写入都是异步的,通过 edit 或 updateData 完成。
Q4: 如何在 Java 中使用?
DataStore 基于 Kotlin 协程,在 Java 中使用较复杂。建议:
- 使用 MMKV(更好的 Java 支持)
- 或者通过 LiveData 转换
Q5: 多进程性能如何?
DataStore 多进程通过 ContentProvider + FileLock 实现,有一定性能开销。如果需要高频多进程访问,建议使用 MMKV。