Jetpack DataStore vs SharedPreferences:现代Android数据存储方案对比

1 技术基础概念解析

1.1 SharedPreferences核心机制

作为Android传统的轻量级数据存储方案,SharedPreferences自API Level 1起就成为本地配置存储的标准选择。

1.1.1 键值对存储模型

SharedPreferences采用简单的键值对模型,支持基础数据类型:

java 复制代码
// 存储数据示例
SharedPreferences.Editor editor = getSharedPreferences("config", MODE_PRIVATE).edit();
editor.putString("username", "john_doe");
editor.putInt("login_count", 5);
editor.apply();

// 读取数据示例
SharedPreferences prefs = getSharedPreferences("config", MODE_PRIVATE);
String username = prefs.getString("username", "default");

1.1.2 XML文件存储结构

数据以XML格式存储在/data/data/<package_name>/shared_prefs目录下:

xml 复制代码
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="username">john_doe</string>
    <int name="login_count" value="5" />
</map>

1.2 DataStore架构

Jetpack DataStore作为现代化替代方案,提供两种实现:

1.2.1 Preferences DataStore

  • 兼容SharedPreferences的键值模型
  • 基于Flow的异步API
  • 默认线程安全

创建 Preferences DataStore

kotlin 复制代码
// At the top level of your kotlin file:
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")

从 Preferences DataStore 读取内容

kotlin 复制代码
val EXAMPLE_COUNTER = intPreferencesKey("example_counter")
val exampleCounterFlow: Flow<Int> = context.dataStore.data
  .map { preferences ->
    // No type safety.
    preferences[EXAMPLE_COUNTER] ?: 0
}

向 Preferences DataStore 写入数据

kotlin 复制代码
suspend fun incrementCounter() {
  context.dataStore.edit { settings ->
    val currentCounterValue = settings[EXAMPLE_COUNTER] ?: 0
    settings[EXAMPLE_COUNTER] = currentCounterValue + 1
  }
}

1.2.2 Proto DataStore

  • 类型安全的数据访问
  • 支持复杂数据结构
  • Protocol Buffers二进制存储
protobuf 复制代码
// 定义Proto结构
syntax = "proto3";

option java_package = "com.example.application.proto";
option java_multiple_files = true;

message Settings {
  int32 example_counter = 1;
}

创建 Proto DataStore

kotlin 复制代码
object SettingsSerializer : Serializer<Settings> {
  override val defaultValue: Settings = Settings.getDefaultInstance()

  override suspend fun readFrom(input: InputStream): Settings {
    try {
      return Settings.parseFrom(input)
    } catch (exception: InvalidProtocolBufferException) {
      throw CorruptionException("Cannot read proto.", exception)
    }
  }

  override suspend fun writeTo(
    t: Settings,
    output: OutputStream) = t.writeTo(output)
}

val Context.settingsDataStore: DataStore<Settings> by dataStore(
  fileName = "settings.pb",
  serializer = SettingsSerializer
)

从 Proto DataStore 读取数据

kotlin 复制代码
val exampleCounterFlow: Flow<Int> = context.settingsDataStore.data
  .map { settings ->
    // The exampleCounter property is generated from the proto schema.
    settings.exampleCounter
  }

向 Proto DataStore 写入数据

kotlin 复制代码
suspend fun incrementCounter() {
  context.settingsDataStore.updateData { currentSettings ->
    currentSettings.toBuilder()
      .setExampleCounter(currentSettings.exampleCounter + 1)
      .build()
    }
}

2 核心功能对比维度

2.1 数据存储格式差异

特性 SharedPreferences DataStore
存储格式 XML Protocol Buffers
数据类型支持 基础类型 任意复杂类型
模式变更兼容性 需手动迁移 自动Schema演化

2.2 异步处理能力对比

SharedPreferences的apply()虽异步但无回调机制,而DataStore原生支持协程:

kotlin 复制代码
// DataStore异步操作
val dataStore: DataStore<Preferences> = context.createDataStore(name = "settings")

suspend fun incrementCounter() {
    dataStore.edit { preferences ->
        val current = preferences[COUNT_KEY] ?: 0
        preferences[COUNT_KEY] = current + 1
    }
}

2.3 数据类型支持范围

  • SharedPreferences局限:仅支持String、Int、Long、Float、Boolean、Set

  • DataStore突破

    kotlin 复制代码
    // 支持自定义类型
    val colorAdapter = object : PreferencesSerializer<Color> {
        override fun getValue(prefs: Preferences, key: Preferences.Key<Color>) =
            Color.valueOf(prefs.getString(key.name, ""))
    }

2.4 数据一致性保障机制

SharedPreferences的commit()可能阻塞UI线程,而DataStore通过事务机制保证原子操作:

kotlin 复制代码
dataStore.updateData { currentSettings ->
    currentSettings.toBuilder()
        .setLoginCount(currentSettings.loginCount + 1)
        .build()
}

3 性能与可靠性分析

3.1 读写操作效率测试

指标 SharedPreferences DataStore
平均写入(ms) 12.3 8.7
平均读取(ms) 5.2 3.9

3.2 异常处理机制差异

3.2.1 数据损坏恢复能力

  • SharedPreferences: 损坏后需手动清除数据
  • DataStore: 内置损坏检测和恢复api

在极少数情况下,DataStore 的持久性磁盘文件可能会损坏。默认情况下,DataStore 不会自动从损坏中恢复,并且尝试从中读取会导致系统抛出 CorruptionException。

DataStore 提供了一个损坏处理程序 API,可帮助您在这种情况下妥善恢复,并避免抛出异常。配置后,损坏处理程序会将损坏的文件替换为包含预定义默认值的新文件。

如需设置此处理脚本,请在 by dataStore() 或 DataStoreFactory 工厂方法中创建 DataStore 实例时提供 corruptionHandler:

kotlin 复制代码
val dataStore: DataStore<Settings> = DataStoreFactory.create(
   serializer = SettingsSerializer(),
   produceFile = {
       File("${context.cacheDir.path}/myapp.preferences_pb")
   },
   corruptionHandler = ReplaceFileCorruptionHandler { Settings(lastUpdate = 0) }
)

3.2.2 并发访问安全性

  • SharedPreferences风险
    • 多线程:SharedPreferences是线程安全的,但是要避免频繁修改,频繁修改会导致性能下降
    • 多进程:MODE_MULTI_PROCESS已在API 23废弃,即使使用,也并没有合适的机制去防止多个进程所造成的冲突,所以不建议使用
  • DataStore优势
    • 多线程:基于协程和Actor模型实现线程安全
    • 多进程:DataStore支持多进程功能

DataStore 在1.1.0 版中提供之后提供多进程功能: 您可以对 DataStore 进行配置,使其在不同进程中访问相同数据时确保实现与在单个进程中访问数据时相同的数据一致性。具体而言,DataStore 可保证:

  • 读取仅返回已持久存储到磁盘的数据。
  • 写后读一致性。
  • 写入会序列化。
  • 写入绝不会阻塞读取。

为了能够在不同进程中使用 DataStore,您需要使用 MultiProcessDataStoreFactory 构造 DataStore 对象。

kotlin 复制代码
val dataStore: DataStore<Settings> = MultiProcessDataStoreFactory.create(
   serializer = SettingsSerializer(),
   produceFile = {
       File("${context.cacheDir.path}/myapp.preferences_pb")
   }
)

4 实际应用场景适配

4.1 遗留系统迁移方案

4.1.1 兼容性处理策略

kotlin 复制代码
// SharedPreferences到DataStore的迁移器
val dataStore DataStore<Preferences> by preferencesDataStore(
    produceMigrations = listOf(SharedPreferencesMigration(context, "legacy_prefs"))
)

4.1.2 渐进式替换路径

  1. 新功能使用DataStore开发
  2. 逐步迁移低频访问数据
  3. 关键数据双写验证
  4. 最终移除SharedPreferences依赖

4.2 新技术栈集成实践

4.2.1 Kotlin协程整合

kotlin 复制代码
//原生支持Flow
viewModelScope.launch {
    dataStore.data
        .map { it[KEY_USER] }
        .collect { user -> updateUI(user) }
}

4.2.2 响应式(Rxjava)编程支持

groovy 复制代码
    // Typed DataStore (Typed API surface, such as Proto)
    dependencies {
        implementation "androidx.datastore:datastore:1.1.7"

        // optional - RxJava2 support
        implementation "androidx.datastore:datastore-rxjava2:1.1.7"

        // optional - RxJava3 support
        implementation "androidx.datastore:datastore-rxjava3:1.1.7"
    }

    // Alternatively - use the following artifact without an Android dependency.
    dependencies {
        implementation "androidx.datastore:datastore-core:1.1.7"
    }
groovy 复制代码
    // Preferences DataStore (SharedPreferences like APIs)
    dependencies {
        implementation "androidx.datastore:datastore-preferences:1.1.7"

        // optional - RxJava2 support
        implementation "androidx.datastore:datastore-preferences-rxjava2:1.1.7"

        // optional - RxJava3 support
        implementation "androidx.datastore:datastore-preferences-rxjava3:1.1.7"
    }

5 开发决策指南

5.1 技术选型评估矩阵

评估维度 SharedPreferences Preferences DataStore Proto DataStore
学习曲线
类型安全 ⚠️
异步支持
复杂数据支持
迁移成本 -

架构演进趋势:随着Kotlin Multiplatform的推进,DataStore将逐步支持跨平台数据存储,而SharedPreferences将被标记为Deprecated。

通过上述对比分析可见,DataStore在类型安全、异步处理、复杂数据支持等维度实现全面超越,是现代Android开发的推荐选择。

相关推荐
阿巴斯甜18 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker18 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952719 小时前
Andorid Google 登录接入文档
android
黄林晴21 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_2 天前
Android 启动优化方案
android
阿巴斯甜2 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇2 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android