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 响应式:数据变化自动通知所有观察者,无需手动注册监听器。
  • 迁移安全:提供 SharedPreferencesMigration API,可保留历史数据。
注意:DataStore 不适合大对象存储(如图片字节流)、高频率写操作(每秒数十次以上建议改用 Room)。数据损坏时默认使用 defaultValue,自定义序列化器应实现 readFrom 异常处理。
DataStore 架构与数据流 App Code (ViewModel / Service) Preferences DataStore Proto DataStore Serializer (序列化/反序列化) 协议: Protobuf / Key-Value Disk (文件) *.preferences_pb / *.pb Flow 观察 ✦ App 通过 .data 属性获取 Flow ✦ 写入使用 edit{} 或 updateData{} ✦ 序列化器确保数据完整性 ✦ 只写入修改的字段(差分) ✦ 原子文件替换,无脏写 ✦ 自动处理并发 选型原则:简单键值对 → Preferences DataStore;结构化数据 → Proto DataStore