DataSource详解以及优势

2 阅读5分钟

目录


一、概述

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 存储位置

/data/data/包名/files/datastore/xxx.preferences_pb

二、添加依赖

2.1 build.gradle 配置

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 实例

方式一:扩展属性(推荐)

// 在顶层文件或单独的文件中定义
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")
        )
    }
)

方式二:单例模式

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

/**
 * 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 写入数据

// 写入单个数据
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 响应式读取(推荐)

// 单个值
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
        )
    }

方式二:一次性读取

suspend fun getUserName(context: Context): String {
    return context.dataStore.data.first()[PreferenceKeys.USER_NAME] ?: ""
}

方式三:带异常处理的读取

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 删除数据

// 删除单个 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 监听数据变化

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 文件

// 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

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

val Context.userStore: DataStore<User> by dataStore(
    fileName = "user.pb",
    serializer = UserSerializer
)

4.4 读写数据

// 读取数据
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()
    }
}

五、完整封装工具类

/**
 * 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))
    }
}

使用示例

// 在 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 数据加载流程

首次访问 data 属性:
┌──────────────────────────────────────────────────────────────────┐
│  1. 创建 Flow                                                      │
│     ↓                                                             │
│  2. 检查缓存                                                       │
│     ├── 有缓存 → 直接返回 Flow                                    │
│     └── 无缓存 → 继续加载                                         │
│         ↓                                                         │
│  3. 执行迁移(如果配置了)                                         │
│     SharedPreferencesMigration:                                    │
│       - 读取旧 SP 数据                                            │
│       - 合并到 DataStore                                          │
│       - 删除旧 SP 文件                                            │
│     ↓                                                             │
│  4. 读取文件                                                       │
│     ├── 文件存在 → 解析 ProtoBuf                                  │
│     ├── 文件不存在 → 返回 defaultValue                            │
│     └── 文件损坏 → 触发 CorruptionHandler                         │
│     ↓                                                             │
│  5. 更新缓存并发送给订阅者                                         │
└──────────────────────────────────────────────────────────────────┘

6.2 数据写入流程

edit {} 或 updateData {} 调用:
┌──────────────────────────────────────────────────────────────────┐
│  1. 获取当前数据(等待进行中的写入完成)                           │
│     ↓                                                             │
│  2. 执行转换(调用传入的 lambda)                                  │
│     ↓                                                             │
│  3. 更新内存缓存(立即生效)                                       │
│     ↓                                                             │
│  4. 发送新数据给订阅者                                             │
│     ↓                                                             │
│  5. 写入磁盘                                                       │
│     ├── a. 创建临时文件: file.tmp                                 │
│     ├── b. 序列化数据写入临时文件                                 │
│     ├── c. 调用 FileChannel.force(true) 刷新到磁盘               │
│     ├── d. 原子重命名: file.tmp → file                           │
│     └── e. 删除备份文件                                           │
│     ↓                                                             │
│  6. 完成                                                           │
└──────────────────────────────────────────────────────────────────┘

七、数据完整性保证机制

7.1 原子写入机制

为什么需要原子写入?
┌──────────────────────────────────────────────────────────────────┐
│  直接写入的问题:                                                  │
│  原文件: [完整数据]                                               │
│       ↓ 直接写入                                                  │
│  写入中: [一半新数据][一半旧数据]  ← 崩溃!                       │
│       ↓                                                           │
│  结果:   [损坏的数据]  无法恢复                                   │
└──────────────────────────────────────────────────────────────────┘

DataStore 的原子写入:
┌──────────────────────────────────────────────────────────────────┐
│  步骤 1:写入临时文件                                              │
│  原文件: [完整数据]                                               │
│  临时文件: [新数据]  (独立文件)                                   │
│                                                                   │
│  步骤 2:原子重命名                                                │
│  file.renameTo(temp) 是原子操作:                                 │
│  • 要么成功:临时文件完全替换原文件                               │
│  • 要么失败:原文件保持不变                                       │
│  • 不会出现"半替换"状态                                           │
│                                                                   │
│  步骤 3:崩溃恢复                                                  │
│  • 写入前崩溃:原文件完整,临时文件不存在                         │
│  • 写入后崩溃:原文件完整,临时文件被忽略                         │
│  • 重命名后崩溃:新文件完整                                       │
└──────────────────────────────────────────────────────────────────┘

7.2 fsync 确保持久化

// DataStore 在写入时会调用
stream.channel.force(true)  // 等同于 fsync

// 效果:强制将数据从操作系统缓存刷入磁盘
// 代价:写入变慢,但数据安全

7.3 CorruptionHandler 损坏恢复

// 使用内置实现
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

val Context.multiProcessDataStore: DataStore<Preferences> by multiProcessDataStore(
    fileName = "shared_config.preferences_pb",
    corruptionHandler = ReplaceFileCorruptionHandler { emptyPreferences() }
)

8.2 多进程同步原理

核心机制:
┌──────────────────────────────────────────────────────────────────┐
│  1. 文件锁 (FileLock)                                             │
│     • 跨进程互斥                                                  │
│     • 保证写入原子性                                              │
│                                                                   │
│  2. 内容观察者 (ContentObserver)                                   │
│     • 监听文件变化                                                │
│     • 其他进程修改时收到通知                                      │
│                                                                   │
│  3. 主动轮询(兜底)                                               │
│     • 定期检查文件修改时间                                        │
│     • 防止通知丢失                                                │
└──────────────────────────────────────────────────────────────────┘

同步流程:
进程 A (写入)                    进程 B (读取)
┌─────────────────┐            ┌─────────────────┐
│ 1. 获取文件锁   │            │                 │
│ 2. 写入数据     │            │                 │
│ 3. 释放锁       │───────────>│ ContentObserver │
│ 4. 通知变化     │            │ 被触发          │
└─────────────────┘            │ 重新加载数据    │
                               │ 更新 Flow       │
                               └─────────────────┘

8.3 多进程注意事项

// 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 读写性能

读取性能:
• 首次读取:需要解析 ProtoBuf 文件,较慢
• 后续读取:直接从内存读取,极快
• Flow 订阅:数据变化时自动通知,无额外开销

写入性能:
• 内存更新:同步,立即生效
• 磁盘写入:异步,在 IO 线程
• fsync 开销:每次写入都会 fsync,保证数据安全

优化建议:
• 批量写入,减少 edit() 调用次数
• 不要在循环中频繁写入
• 使用 distinctUntilChanged() 减少重复通知

十、最佳实践

10.1 项目结构

app/
├── src/main/java/com/example/app/
│   ├── data/
│   │   ├── datastore/
│   │   │   ├── DataStoreHelper.kt      # 工具类
│   │   │   └── PreferenceKeys.kt       # Key 定义
│   │   └── proto/
│   │       └── user.proto              # Proto 定义
│   └── ...

10.2 Key 管理

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 集成

// 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 异常处理

// 安全读取
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 迁移

// 方式一:自动迁移(推荐)
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

特性SharedPreferencesDataStoreMMKV
API 风格同步异步 (Flow + 协程)同步 + 回调
类型安全
数据完整性△ (可能丢失)✓ (原子写入)✓ (mmap)
多进程支持✗ (已废弃)✓ (原生)
ANR 风险✓ (有)△ (低)✗ (无)
性能极高
Jetpack 集成
官方支持✗ (腾讯)

12.2 选型建议

场景推荐
新项目DataStore (Preferences 或 Proto)
高性能需求MMKV
多进程高频访问MMKV
旧项目维护SharedPreferences
需要 Jetpack 集成DataStore

─────────────────────────────────────────────────────────────────────────────────┐
│                    核心特性对比                                                  │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   特性              │  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 的概念。所有写入都是异步的,通过 editupdateData 完成。

Q4: 如何在 Java 中使用?

DataStore 基于 Kotlin 协程,在 Java 中使用较复杂。建议:

  • 使用 MMKV(更好的 Java 支持)
  • 或者通过 LiveData 转换

Q5: 多进程性能如何?

DataStore 多进程通过 ContentProvider + FileLock 实现,有一定性能开销。如果需要高频多进程访问,建议使用 MMKV。


附录:参考资料