DataStore:是什么、数据存储基础以及 SharedPreferences 的替代方案

作者: IT Sectr 发布日期: 2026-03-12 阅读时间: 9 分钟

DataStore — 来自 Jetpack 库的组件,用于在 Android 应用中存储少量数据。与 SharedPreferences 不同,它异步工作,并保证在并发访问时数据的一致性。根据 Google, 2024 的数据,DataStore 使用 Kotlin Coroutines 和 Flow,使其对主线程安全并适用于响应式架构。

要点

  • DataStore — SharedPreferences 的替代方案,具有异步 API 和通过 Protocol Buffers 的数据类型
  • Preferences DataStore — 通过 Flow 进行读取和事务的简单 Key-Store
  • Proto DataStore — 具有自动模式迁移的类型化存储
  • SharedPreferences — 同步 API,在大数据量时阻塞 UI 线程
  • 迁移 通过 SharedPreferencesMigration 特殊接口执行,不会丢失数据

什么是 DataStore?

DataStore — Google 在 Android 中用于本地数据存储的解决方案,于 2020 年作为 SharedPreferences 的替代方案推出。它支持两种模式:Preferences DataStore(简单的键值对)和 Proto DataStore(基于 Protocol Buffers 的类型化模式)。

主要优势 — 完全异步:所有读取操作从 Kotlin Coroutines 返回 Flow,写入在协程上下文中执行。这消除了主线程阻塞,这是 SharedPreferences 在处理大量数据时的典型问题。

DataStore 保证操作的原子性:由于事务模型,并发写入不会导致数据丢失。如果两个组件同时修改同一值,DataStore 通过 compare-and-swap 机制正确处理冲突。

根据 Google I/O 2023 的数据,DataStore 在 40% 的 Android 新项目中使用,Google 建议在需要设置存储稳定性的所有应用中从 SharedPreferences 迁移。

DataStore 架构

DataStore 的基础是 SingleProcessDataStore — 在单个进程范围内工作的实现。它使用带有文件级锁的文件存储:在写入数据时,文件被锁定,防止并发访问时的损坏。

DataStore 自动处理反序列化错误:如果文件损坏,它返回默认值并重写文件。此行为通过 corruptionHandler 配置,该处理程序可在创建 DataStore 时设置。

DataStore 解决的 SharedPreferences 问题

SharedPreferences 存在三个基本问题:主线程上的同步磁盘读取、并发写入时缺乏原子性保证以及无法响应式跟踪更改。DataStore 解决了所有三个问题:Flow 用于观察,文件锁用于原子性,异步 API 用于线程安全。

DataStore 在 Android 中如何工作?

DataStore 将数据存储在设备内部存储的文件中。Preferences DataStore 使用与 SharedPreferences 类似的文件格式,但具有用于完整性检查的额外元数据。Proto DataStore 使用 Protocol Buffers 的二进制格式,这减小了文件大小并加快了序列化速度。

在读取数据时,DataStore 一次性将整个文件加载到内存中,之后订阅者通过 Flow 接收当前状态。更改自动传输给所有活动订阅者 — 不需要像 SharedPreferences 那样手动注册监听器。

Preferences DataStore 的工作原理

Preferences DataStore 使用基于 Map 的内置序列化机制。每个条目是一个字符串和原始类型(Int、Boolean、Float、Long、String、Set)的对。数据存储在 XML 文件中,类似于 SharedPreferences,但通过文件锁实现原子写入。

创建 Preferences DataStore 的示例:Context 上的 preferencesDataStore 扩展创建一个带有文件名的单例。在重复调用时返回同一实例 — 这消除了文件重复和不同存储实例的混淆。

Proto DataStore 的工作原理

Proto DataStore 需要通过 .proto 文件定义数据模式并使用 protobuf 插件进行编译。生成的 Java 类用作所有字段的唯一入口点 — 这消除了 SharedPreferences 中常见的键拼写错误。

Proto DataStore 模式定义一次,并支持在不丢失旧数据的情况下添加新字段。如果在应用程序的新版本中添加具有默认值的字段,旧文件将被正确反序列化 — 向后兼容性 已内置于协议中。

Preferences DataStore 和 Proto DataStore:比较

Preferences DataStoreProto DataStore 之间的选择取决于数据的复杂性和类型化要求。两种变体都是异步和事务性的,但在类型安全级别和序列化性能上有所不同。

特性Preferences DataStoreProto DataStore
类型化弱(键值)强(生成的类)
序列化XML(内置)Protocol Buffers(protobuf)
文件大小大(可读 XML)小(二进制)
复杂性低(无 .proto)中(需要 .proto)
模式迁移无模式自动(proto)
兼容性SharedPreferences(通过迁移)仅 Proto DataStore

何时选择 Preferences DataStore

Preferences DataStore 适用于简单设置:功能启用标志、授权令牌字符串、应用程序启动次数。如果数据量少(最多 10–15 个键)且不需要严格模式 — Preferences DataStore 提供最低入门门槛,无需连接 protobuf 插件。

何时选择 Proto DataStore

Proto DataStore 在数据结构复杂或可能在应用程序版本之间变化时是合理的。例如,用户配置文件设置或具有 20+ 字段的 A/B 测试配置。Protobuf 提供强类型化和自动迁移,这消除了因键不匹配而导致的运行时错误。

如何从 SharedPreferences 迁移到 DataStore

Google 通过 SharedPreferencesMigration 类提供内置迁移机制。迁移在应用程序更新后的首次启动时执行一次:DataStore 从 SharedPreferences 读取数据,以自身格式写入,并将迁移标记为完成。

迁移支持自定义转换:如果 SharedPreferences 中的键与所需的 DataStore 键不匹配,可以通过 SharedPreferencesMigration 定义转换函数。这允许在迁移过程中重命名键和更改数据类型。

逐步迁移

第一步:将 DataStore 添加到 build.gradle 并创建带有迁移的 DataStore 实例:SharedPreferencesMigration 接受 SharedPreferences 文件名和要转移的键集。第二步:删除所有通过 SharedPreferences 工作的代码,并将其替换为 DataStore 调用。第三步 — 测试迁移:在首次启动时,数据应出现在 DataStore 中,旧的 SharedPreferences 文件应停止使用。

kotlin
val Context.dataStore by preferencesDataStore(
    name = "settings",
    produceMigrations = { context ->
        listOf(
            SharedPreferencesMigration(context, "old_prefs")
        )
    }
)

DataStore 在代码中的使用示例

DataStore 易于集成到现有项目中。以下是 Preferences DataStore 和 Proto DataStore 的实用示例 — 两者都演示了数据的读取、写入和响应式观察。

Preferences DataStore:读取和写入设置

在此示例中,Preferences DataStore 存储三个设置:深色主题、用户名和启动次数。读取通过返回 Flow 的 .data 扩展执行。写入 — 通过 .edit 挂起函数,保证更改的原子性。

kotlin
val Context.settingsDataStore by preferencesDataStore(name = "settings")

val isDarkMode: Flow<Boolean> = settingsDataStore.data
    .map { preferences ->
        preferences[booleanPreferencesKey("dark_mode")] ?: false
    }

suspend fun toggleDarkMode() {
    settingsDataStore.edit { prefs ->
        val current = prefs[booleanPreferencesKey("dark_mode")] ?: false
        prefs[booleanPreferencesKey("dark_mode")] = !current
    }
}

Proto DataStore:模式和使用

Proto DataStore 需要定义 .proto 文件。编译后,创建用于读取和写入的 UserSettings 类。模式版本的迁移在同一个 .proto 文件中描述并自动应用。

kotlin
// user_preferences.proto
syntax = "proto3";

message UserPreferences {
    string display_name = 1;
    int32 notification_count = 2;
    bool notifications_enabled = 3;
}

// 从 DataStore 读取
val userPreferencesFlow: Flow<UserPreferences> =
    protoDataStore.data

// 写入新值
suspend fun updateDisplayName(name: String) {
    protoDataStore.updateData { prefs ->
        prefs.toBuilder()
            .setDisplayName(name)
            .build()
    }
}

响应式观察更改

DataStore 通过 ViewModel 与 MVVM 架构集成。来自 DataStore 的 Flow 通过 .stateIn 收集并在 UI 中使用。每次数据更改时,UI 自动更新 — 不需要手动更新或 LiveData。

kotlin
class SettingsViewModel(
    private val dataStore: DataStore<Preferences>
) : ViewModel() {

    val uiState: StateFlow<SettingsUiState> =
        dataStore.data
            .map { prefs ->
                SettingsUiState(
                    isDarkMode = prefs[booleanPreferencesKey("dark_mode")] ?: false,
                    counter = prefs[intPreferencesKey("launch_count")] ?: 0
                )
            }
            .stateIn(
                scope = viewModelScope,
                started = SharingStarted.WhileSubscribed(5000),
                initialValue = SettingsUiState()
            )
}

常见问题

DataStore 比 SharedPreferences 好在哪里?

DataStore 异步工作(不阻塞 UI 线程),通过事务支持并发访问,并允许通过 Flow 响应式订阅更改。SharedPreferences — 同步 API,大数据量时有 ANR 风险,且没有内置的响应式支持。

DataStore 可以与 Java 一起使用吗?

DataStore 是用 Kotlin 编写的,需要 Kotlin Coroutines。从 Java 使用它是可能的,但不方便:必须使用 CompletableFuture 创建包装器或手动管理协程。对于 Java 项目,Google 建议保留 SharedPreferences 或向模块添加 Kotlin。

DataStore 适合存储大量数据吗?

DataStore 在读取时将整个文件加载到内存中,因此不适合存储列表或大型对象。对于此类场景,请使用 Room 或 SQLite。DataStore 针对设置和小型结构化数据进行了优化 — 可达数百 KB。

如何处理 DataStore 文件损坏错误?

在创建 DataStore 时可以传递 corruptionHandler — 在文件损坏时调用的函数。默认情况下,DataStore 抛出 CorruptionException。在 corruptionHandler 中可以返回空数据,之后 DataStore 将用正确状态重写文件。

Proto DataStore 是否强制需要 .proto 文件?

是的,Proto DataStore 需要在 .proto 文件中定义模式并连接 protobuf-gradle-plugin。如果项目较小且数据简单,使用 Preferences DataStore 更容易 — 它不需要额外的构建配置。

总结

  • DataStore — SharedPreferences 的现代替代品,与 Kotlin Coroutines 和 Flow 一起使用
  • Preferences DataStore — 无模式的简单键值,适用于设置
  • Proto DataStore — 具有 protobuf 模式和自动迁移的类型化存储
  • 迁移 通过 SharedPreferencesMigration 内置从 SharedPreferences 到 DataStore
  • 线程安全 — 所有操作都是异步的,排除了 UI 阻塞
  • 响应式 — Flow 在每次数据更改时通知订阅者
  • 建议 — 在所有新的 Android 项目中使用 DataStore,在处理设置时迁移现有项目

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读