DataStore #

一、DataStore概述 #

DataStore是Jetpack提供的新一代数据存储解决方案,用于替代SharedPreferences。它提供了更好的数据一致性、异步API和类型安全。

1.1 DataStore vs SharedPreferences #

特性 DataStore SharedPreferences
异步API ✅ 协程/Flow ❌ 同步
数据一致性 ✅ 事务性 ❌ 可能不一致
类型安全 ✅ Proto DataStore ❌ 无
错误处理 ✅ 异常处理 ❌ 忽略错误
迁移支持 ✅ 自动迁移 -

1.2 DataStore类型 #

类型 说明 适用场景
Preferences DataStore 键值对存储 简单配置
Proto DataStore 类型化对象存储 复杂数据结构

1.3 添加依赖 #

kotlin
dependencies {
    // Preferences DataStore
    implementation("androidx.datastore:datastore-preferences:1.0.0")
    
    // Proto DataStore
    implementation("androidx.datastore:datastore:1.0.0")
}

二、Preferences DataStore #

2.1 创建DataStore #

kotlin
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")

2.2 定义键 #

kotlin
// 基本类型
val KEY_NAME = stringPreferencesKey("name")
val KEY_AGE = intPreferencesKey("age")
val KEY_IS_LOGIN = booleanPreferencesKey("is_login")
val KEY_SCORE = floatPreferencesKey("score")
val KEY_TIMESTAMP = longPreferencesKey("timestamp")

// Set类型
val KEY_TAGS = stringSetPreferencesKey("tags")

2.3 存储数据 #

kotlin
class SettingsManager(private val context: Context) {
    
    private val dataStore = context.dataStore
    
    suspend fun saveName(name: String) {
        dataStore.edit { preferences ->
            preferences[KEY_NAME] = name
        }
    }
    
    suspend fun saveAge(age: Int) {
        dataStore.edit { preferences ->
            preferences[KEY_AGE] = age
        }
    }
    
    suspend fun saveUserInfo(name: String, age: Int, isLogin: Boolean) {
        dataStore.edit { preferences ->
            preferences[KEY_NAME] = name
            preferences[KEY_AGE] = age
            preferences[KEY_IS_LOGIN] = isLogin
        }
    }
}

2.4 读取数据 #

kotlin
class SettingsManager(private val context: Context) {
    
    private val dataStore = context.dataStore
    
    // 读取单个值
    val nameFlow: Flow<String> = dataStore.data.map { preferences ->
        preferences[KEY_NAME] ?: ""
    }
    
    val ageFlow: Flow<Int> = dataStore.data.map { preferences ->
        preferences[KEY_AGE] ?: 0
    }
    
    val isLoginFlow: Flow<Boolean> = dataStore.data.map { preferences ->
        preferences[KEY_IS_LOGIN] ?: false
    }
    
    // 读取多个值
    data class UserSettings(
        val name: String = "",
        val age: Int = 0,
        val isLogin: Boolean = false
    )
    
    val userSettingsFlow: Flow<UserSettings> = dataStore.data.map { preferences ->
        UserSettings(
            name = preferences[KEY_NAME] ?: "",
            age = preferences[KEY_AGE] ?: 0,
            isLogin = preferences[KEY_IS_LOGIN] ?: false
        )
    }
}

2.5 在Activity中使用 #

kotlin
class MainActivity : AppCompatActivity() {
    
    private val settingsManager by lazy { SettingsManager(this) }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // 观察数据变化
        lifecycleScope.launch {
            settingsManager.userSettingsFlow.collect { settings ->
                binding.tvName.text = settings.name
                binding.tvAge.text = settings.age.toString()
            }
        }
        
        // 保存数据
        binding.btnSave.setOnClickListener {
            lifecycleScope.launch {
                settingsManager.saveUserInfo("张三", 25, true)
            }
        }
    }
}

2.6 处理异常 #

kotlin
val userSettingsFlow: Flow<UserSettings> = dataStore.data
    .catch { exception ->
        if (exception is IOException) {
            emit(emptyPreferences())
        } else {
            throw exception
        }
    }
    .map { preferences ->
        UserSettings(
            name = preferences[KEY_NAME] ?: "",
            age = preferences[KEY_AGE] ?: 0
        )
    }

2.7 清除数据 #

kotlin
suspend fun clearAll() {
    dataStore.edit { preferences ->
        preferences.clear()
    }
}

suspend fun clearKey(key: Preferences.Key<*>) {
    dataStore.edit { preferences ->
        preferences.remove(key)
    }
}

三、Proto DataStore #

3.1 定义Proto文件 #

protobuf
// src/main/proto/settings.proto
syntax = "proto3";

option java_package = "com.example.myapp";
option java_multiple_files = true;

message Settings {
  string name = 1;
  int32 age = 2;
  bool is_login = 3;
}

3.2 添加Proto依赖 #

kotlin
plugins {
    id("com.google.protobuf") version "0.9.4"
}

dependencies {
    implementation("androidx.datastore:datastore:1.0.0")
    implementation("com.google.protobuf:protobuf-javalite:3.24.0")
}

protobuf {
    protoc {
        artifact = "com.google.protobuf:protoc:3.24.0"
    }
    generateProtoTasks {
        all().forEach { task ->
            task.builtins {
                create("java") {
                    option("lite")
                }
            }
        }
    }
}

3.3 创建Serializer #

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

3.4 创建DataStore #

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

3.5 存储数据 #

kotlin
class ProtoSettingsManager(private val context: Context) {
    
    private val dataStore = context.settingsDataStore
    
    suspend fun updateSettings(name: String, age: Int, isLogin: Boolean) {
        dataStore.updateData { settings ->
            settings.toBuilder()
                .setName(name)
                .setAge(age)
                .setIsLogin(isLogin)
                .build()
        }
    }
    
    suspend fun updateName(name: String) {
        dataStore.updateData { settings ->
            settings.toBuilder()
                .setName(name)
                .build()
        }
    }
}

3.6 读取数据 #

kotlin
class ProtoSettingsManager(private val context: Context) {
    
    private val dataStore = context.settingsDataStore
    
    val settingsFlow: Flow<Settings> = dataStore.data
        .catch { exception ->
            if (exception is IOException) {
                emit(Settings.getDefaultInstance())
            } else {
                throw exception
            }
        }
    
    val nameFlow: Flow<String> = dataStore.data.map { it.name }
    val ageFlow: Flow<Int> = dataStore.data.map { it.age }
    val isLoginFlow: Flow<Boolean> = dataStore.data.map { it.isLogin }
}

四、从SharedPreferences迁移 #

4.1 Preferences DataStore迁移 #

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

4.2 Proto DataStore迁移 #

kotlin
private val Context.settingsDataStore: DataStore<Settings> by dataStore(
    fileName = "settings.pb",
    serializer = SettingsSerializer,
    produceMigrations = { context ->
        listOf(
            SharedPreferencesMigration(
                context,
                "old_prefs_name"
            ) { sharedPrefs: SharedPreferencesView, currentData: Settings ->
                currentData.toBuilder()
                    .setName(sharedPrefs.getString("name", "") ?: "")
                    .setAge(sharedPrefs.getInt("age", 0))
                    .setIsLogin(sharedPrefs.getBoolean("is_login", false))
                    .build()
            }
        )
    }
)

五、封装工具类 #

kotlin
object DataStoreUtils {
    
    private lateinit var dataStore: DataStore<Preferences>
    
    fun init(context: Context) {
        dataStore = context.dataStore
    }
    
    suspend fun <T> put(key: Preferences.Key<T>, value: T) {
        dataStore.edit { preferences ->
            preferences[key] = value
        }
    }
    
    fun <T> get(key: Preferences.Key<T>, defaultValue: T): Flow<T> {
        return dataStore.data.map { preferences ->
            preferences[key] ?: defaultValue
        }
    }
    
    suspend fun <T> getOnce(key: Preferences.Key<T>, defaultValue: T): T {
        return dataStore.data.map { preferences ->
            preferences[key] ?: defaultValue
        }.first()
    }
    
    suspend fun remove(key: Preferences.Key<*>) {
        dataStore.edit { preferences ->
            preferences.remove(key)
        }
    }
    
    suspend fun clear() {
        dataStore.edit { preferences ->
            preferences.clear()
        }
    }
}

六、最佳实践 #

6.1 使用单例 #

kotlin
class SettingsRepository private constructor(private val dataStore: DataStore<Preferences>) {
    
    companion object {
        @Volatile
        private var instance: SettingsRepository? = null
        
        fun getInstance(context: Context): SettingsRepository {
            return instance ?: synchronized(this) {
                instance ?: SettingsRepository(context.dataStore).also { instance = it }
            }
        }
    }
}

6.2 避免频繁写入 #

kotlin
// 错误:频繁写入
repeat(100) {
    dataStore.edit { preferences ->
        preferences[KEY_COUNT] = it
    }
}

// 正确:批量更新
dataStore.edit { preferences ->
    repeat(100) {
        preferences[KEY_COUNT] = it
    }
}

6.3 使用在Application中初始化 #

kotlin
class MyApp : Application() {
    
    override fun onCreate() {
        super.onCreate()
        DataStoreUtils.init(this)
    }
}

七、总结 #

本章详细介绍了DataStore:

  1. DataStore的基本概念和优势
  2. Preferences DataStore的使用
  3. Proto DataStore的使用
  4. 从SharedPreferences迁移
  5. 工具类封装
  6. 最佳实践

DataStore是SharedPreferences的现代替代方案,推荐在新项目中使用。

最后更新:2026-03-26