3. Preferences DataStore 基础配置:Kotlin 序列化依赖与 SharedPreferences 迁移辅助类

3.1 原理概述

Preferences DataStore 是 Jetpack DataStore 的键值对实现,以类型安全的 Preferences 对象存储数据。它使用 Kotlin 序列化(kotlinx-serialization)将 Set<String> 等复杂类型进行编码/解码,而基本类型(Int、Long、Float、Boolean、String)直接通过代理存取。

从 SharedPreferences 迁移时,DataStore 提供了 SharedPreferencesMigration 辅助类,自动读取旧文件并写入新 DataStore,避免数据丢失。迁移是无损的,且在首次读取 DataStore 时同步执行。

核心依赖项:
  • androidx.datastore:datastore-preferences – Preferences DataStore 核心库
  • org.jetbrains.kotlinx:kotlinx-serialization-json – 用于序列化 Set<String> 等复杂类型
Preferences DataStore 基础配置与迁移流程 添加依赖 创建 DataStore 注册迁移 读取 SharedPreferences 旧文件:{key=value} 位于 data/data/…/shared_prefs SharedPreferencesMigration 读取旧文件 → 写入 DataStore 同步迁移,保证数据完整 Preferences DataStore 新文件:直接键值对读写 无需序列化额外配置 kotlinx-serialization-json 用于 Set<String> 等复杂类型的编码 / 解码

3.2 实现流程(Gradle 依赖配置)

  1. 添加 BOM(可选):确保 DataStore 版本与 Compose/Lifecycle 兼容。
  2. 添加 Preferences DataStore 依赖:在 app/build.gradle.kts 中引入。
  3. 添加 Kotlin 序列化插件:在项目根 build.gradle.kts 应用插件,并在模块中配置。
版本建议:DataStore 1.0.0 及以上版本均支持迁移。推荐使用 1.1.1 或最新稳定版。

3.3 关键代码与配置示例

模块级 build.gradle.kts

plugins {
    id("com.android.application")
    kotlin("android")
    kotlin("plugin.serialization") // 必须应用
}

android {
    // ... 其他配置
}

dependencies {
    // Preferences DataStore
    implementation("androidx.datastore:datastore-preferences:1.1.1")
    // Kotlin 序列化 (用于 Set<String> 等)
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
}

项目级 build.gradle.kts(添加序列化插件 classpath)

plugins {
    id("org.jetbrains.kotlin.plugin.serialization") version "1.9.22" apply false
    // 其他插件
}

3.4 创建 DataStore 实例并注册迁移

在 Application 或单例中创建 DataStore,并通过 SharedPreferencesMigration 迁移旧数据。

// 迁移辅助类:自动将 SharedPreferences 的键值对复制到 DataStore
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(
    name = "my_settings",
    produceMigrations = { context ->
        listOf(SharedPreferencesMigration(context, "old_shared_prefs_name"))
    }
)
迁移配置参数说明
参数类型说明
nameStringDataStore 文件名(无需扩展名)
produceMigrationslambda返回 List<DataMigration<Preferences>>,包含 SharedPreferencesMigration
SharedPreferencesMigration(context, sharedPreferencesName)—第一个参数是 Context,第二个是旧的 SharedPreferences 文件名

3.5 调试与验证

  • 首次启动 App 时,可通过 Logcat 观察迁移日志:DataStore 会输出 Migrating from SharedPreferences...。
  • 使用 Device File Explorer 查看 data/data/<package>/files/…/datastore/ 目录下是否生成新文件。
  • 手动写入旧 SharedPreferences 数据,然后启动 App,检查 DataStore 能否正确读取。
常见错误:
  1. 未应用 kotlin.plugin.serialization 导致 Set<String> 存储时抛出 KotlinReflectionNotSupportedError。
  2. 迁移时指定的 sharedPreferencesName 与实际文件名不匹配(不要带 .xml 后缀)。
  3. 多次调用 preferencesDataStore 创建同一 Name 的实例可能引发 IllegalStateException。

3.6 性能与稳定性优化

  • 迁移过程在 IO 线程同步执行,应避免在主线程首次读取 DataStore(使用 first() 或 data() 会触发挂起)。
  • 如果旧 SharedPreferences 数据量极大(>100KB),建议在 produceMigrations 中配置 CustomMigration 分批迁移,避免 ANR。
  • 始终使用 catch 处理 DataStore 的 IOException,并在读取失败时提供默认值。
最佳实践:将 DataStore 实例声明为顶层委托或 Application 单例,确保全局唯一,并使用 kotlinx.coroutines.flow.Flow 进行响应式读取。

3.7 完整初始化模板(包含迁移与类型安全读取)

import android.content.Context
import android.content.SharedPreferences
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.preferencesDataStore
import androidx.datastore.preferences.core.stringPreferencesKey

private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(
    name = "user_preferences",
    produceMigrations = { context ->
        listOf(SharedPreferencesMigration(context, "legacy_prefs"))
    }
)

// 类型安全键:使用 stringPreferencesKey 而非编码枚举
val KEY_USER_NAME = stringPreferencesKey("user_name")

// 读取示例(在 ViewModel 中使用)
fun getUserName(context: Context): Flow<String?> {
    return context.dataStore.data.map { preferences ->
        preferences[KEY_USER_NAME]
    }
}