DataStore 是 Google 官方替代 SharedPreferences 的轻量本地存储方案,基于Kotlin 协程 + Flow ,全异步、非阻塞、线程安全、事务安全、无数据损坏、编译期类型安全,专门用于存储少量键值数据、配置信息、简单对象。
一、DataStore 核心基础
1. 为什么淘汰 SharedPreferences(SP)
SP 原生痛点全部被解决:
- SP 主线程阻塞、
apply()会隐性卡顿、易 ANR;DataStore 纯协程异步,绝不阻塞主线程 - SP 并发写入易数据丢失、文件损坏 ;DataStore 基于事务原子操作,崩溃也不会脏数据
- SP 无编译期类型安全,运行时易
ClassCastException;DataStore 强类型 Key,编译报错拦截问题 - SP 仅支持基础类型,不支持自定义对象、集合;Proto DataStore 支持任意结构化数据
- SP 监听回调缺陷多;DataStore 基于 Flow,数据变化自动流通知,生命周期安全
2. DataStore 两种实现(必懂)
Google 提供两套 API,适配不同业务场景,日常开发优先用 Preferences DataStore,复杂对象用 Proto DataStore
表格
| 类型 | 特点 | 适用场景 | 数据格式 |
|---|---|---|---|
| Preferences DataStore | 类 SP 键值对、无需定义数据结构、上手快、基础类型全覆盖 | 用户配置、开关、简单键值、SP 迁移 | 键值对(String/Int/Boolean/Long/Float/Double) |
| Proto DataStore | Protobuf 序列化、强类型安全、支持自定义实体 / 枚举 / 列表、结构数据 | 复杂对象、业务数据、需要类型严格校验 | Protobuf 序列化自定义对象 |
3. 核心原理
- 底层基于文件存储,单文件写入,独占锁保证并发安全
- 全部 API 基于协程 + Flow ,无同步阻塞方法,所有读写都在后台线程
- 写入采用原子事务:先写入临时文件,成功后替换原文件,崩溃不损坏原数据
- 单例实例管理,全局唯一,避免多实例文件冲突
二、环境配置(最新稳定版 1.1.7,2026)
在 module 级 build.gradle.kts(app 模块)添加依赖
1. Preferences DataStore(键值版,最常用)
kotlin
scss
// Preferences DataStore(替代SP)
implementation("androidx.datastore:datastore-preferences:1.1.7")
// 可选:RxJava 兼容(不用协程时用)
// implementation("androidx.datastore:datastore-preferences-rxjava3:1.1.7")
2. Proto DataStore(对象版)额外依赖
kotlin
scss
// 基础DataStore核心
implementation("androidx.datastore:datastore:1.1.7")
// protobuf 插件配置(项目根目录build.gradle.kts)
plugins {
id("com.google.protobuf") version "0.9.4" apply false
}
// app模块再引入插件
plugins {
id("com.google.protobuf")
}
三、Preferences DataStore 完整实战(类 SP,最全代码)
日常 90% 场景用这个,和 SP 使用逻辑对齐,迁移零成本。
1. 定义 DataStore 单例(全局唯一)
建议写在 Application 扩展、工具类、单例类 ,全局只初始化一次,用 Context 扩展属性最优雅。
kotlin
kotlin
import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.preferencesDataStore
// Context 扩展属性,全局单例DataStore
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(
name = "app_preferences" // 存储文件名,无需后缀,自动生成文件
)
// 封装工具类,外部直接调用,不用管Context
object DataStorePrefUtils {
// 1. 定义所有Key(编译期类型安全,泛型指定类型,不会类型错乱)
// 布尔型:开关
val IS_LOGIN = booleanPreferencesKey("is_login")
// 字符串:用户ID
val USER_ID = stringPreferencesKey("user_id")
// 整型:年龄
val USER_AGE = intPreferencesKey("user_age")
// 长整型:时间戳
val LAST_LOGIN_TIME = longPreferencesKey("last_login_time")
// 浮点型
val USER_HEIGHT = floatPreferencesKey("user_height")
// 双精度
val USER_WEIGHT = doublePreferencesKey("user_weight")
// 获取全局dataStore实例(需要传入Application Context,避免内存泄漏)
private lateinit var dataStore: DataStore<Preferences>
fun init(context: Context) {
dataStore = context.dataStore
}
// ====================== 数据存储(协程写入)======================
/**
* 单个key写入
*/
suspend fun <T> put(key: Preferences.Key<T>, value: T) {
dataStore.edit { preferences ->
preferences[key] = value
}
}
/**
* 批量写入(一次编辑多个key,效率更高)
*/
suspend fun putAll(block: Preferences.Editor.() -> Unit) {
dataStore.edit(block)
}
// ====================== 数据读取(Flow流,自动监听数据变化)======================
/**
* 读取单个key,返回Flow,数据变化自动回调
* @param key 键
* @param defaultValue 默认值(防止null)
*/
fun <T> get(key: Preferences.Key<T>, defaultValue: T): Flow<T> {
return dataStore.data
.catch {
// 异常捕获:文件损坏、读取异常
emit(emptyPreferences())
}
.map { preferences ->
preferences[key] ?: defaultValue
}
}
// ====================== 删除、清空 ======================
// 删除单个key
suspend fun <T> remove(key: Preferences.Key<T>) {
dataStore.edit {
it.remove(key)
}
}
// 清空所有数据
suspend fun clear() {
dataStore.edit {
it.clear()
}
}
}
2. 初始化(Application 中)
必须用 Application 上下文,不能用 Activity 上下文,防止内存泄漏
kotlin
kotlin
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
// 初始化DataStore工具类
DataStorePrefUtils.init(this)
}
}
清单文件注册 Application
xml
ini
<application
android:name=".MyApp"
...>
</application>
3. Activity/Fragment 中使用(协程环境)
DataStore 所有读写必须在协程作用域 (lifecycleScope、viewModelScope),禁止主线程直接调用挂起函数。
3.1 写入数据(挂起函数)
kotlin
kotlin
// 方式1:单个写入
lifecycleScope.launch {
DataStorePrefUtils.put(DataStorePrefUtils.IS_LOGIN, true)
DataStorePrefUtils.put(DataStorePrefUtils.USER_ID, "10086")
}
// 方式2:批量写入(推荐,一次事务,性能更好)
lifecycleScope.launch {
DataStorePrefUtils.putAll {
this[IS_LOGIN] = true
this[USER_ID] = "10086"
this[USER_AGE] = 25
this[LAST_LOGIN_TIME] = System.currentTimeMillis()
}
}
3.2 读取数据(Flow 流式读取,监听数据实时变化)
kotlin
kotlin
// 读取并监听:只要存储值改变,这里自动收到新值
lifecycleScope.launch {
DataStorePrefUtils.get(DataStorePrefUtils.IS_LOGIN, false)
.collect { isLogin ->
// 每次数据变化都会回调
Log.d("DataStore", "登录状态:$isLogin")
}
}
// 读取单次数据(只拿当前值,不持续监听)
lifecycleScope.launch {
val userId = DataStorePrefUtils.get(DataStorePrefUtils.USER_ID, "").first()
Log.d("DataStore", "用户ID:$userId")
}
3.3 删除、清空
kotlin
csharp
lifecycleScope.launch {
// 删除单个key
DataStorePrefUtils.remove(DataStorePrefUtils.USER_AGE)
// 清空全部
// DataStorePrefUtils.clear()
}
4. ViewModel 中使用(最规范写法)
viewModelScope 生命周期绑定 ViewModel,自动管理协程,无需手动取消
kotlin
kotlin
class MainViewModel : ViewModel() {
// 存储数据
fun saveData() {
viewModelScope.launch {
DataStorePrefUtils.put(DataStorePrefUtils.IS_LOGIN, true)
}
}
// 暴露Flow给UI观察(Compose/Flow布局直接收集)
val loginStateFlow: Flow<Boolean> = DataStorePrefUtils.get(DataStorePrefUtils.IS_LOGIN, false)
}
四、SharedPreferences 一键迁移(官方 API,无痛迁移)
老项目全部 SP 数据,一行代码自动迁移到 DataStore,无需手动遍历键值。
Preferences DataStore 迁移改造
只需要在初始化时添加 migrations 参数
kotlin
ini
import androidx.datastore.preferences.core.migrations
import androidx.datastore.preferences.migrateSharedPreferences
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(
name = "app_preferences",
// 迁移配置:原SP文件名 + 自动迁移所有键值
migrations = listOf(
migrateSharedPreferences(sharedPreferencesName = "sp_config")
)
)
sp_config:你原来 SP 的文件名(getSharedPreferences("sp_config",MODE_PRIVATE))- 原理:首次启动 DataStore 时,自动读取全部 SP 数据写入 DataStore,之后不再读取 SP,完全无缝切换。
五、Proto DataStore 完整实战
等待研究....