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:
- DataStore的基本概念和优势
- Preferences DataStore的使用
- Proto DataStore的使用
- 从SharedPreferences迁移
- 工具类封装
- 最佳实践
DataStore是SharedPreferences的现代替代方案,推荐在新项目中使用。
最后更新:2026-03-26