DataStore的使用:

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. 核心原理

  1. 底层基于文件存储,单文件写入,独占锁保证并发安全
  2. 全部 API 基于协程 + Flow无同步阻塞方法,所有读写都在后台线程
  3. 写入采用原子事务:先写入临时文件,成功后替换原文件,崩溃不损坏原数据
  4. 单例实例管理,全局唯一,避免多实例文件冲突

二、环境配置(最新稳定版 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 所有读写必须在协程作用域lifecycleScopeviewModelScope),禁止主线程直接调用挂起函数

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 完整实战

等待研究....

相关推荐
我命由我123451 天前
Android 开发问题:无法从存储库 “D:\keys\MyNotifications.jks“ 中读取密钥 MyNotifications.
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
BoomHe1 天前
Android (AAOS) 13 编译中间产物(Wifi Jar)
android·android studio·android jetpack
撩得Android一次心动2 天前
Android DataBinding 全面解析【源码篇2】
android·源码·android jetpack·databinding
我命由我123452 天前
Android 开发,getSystemService 警告信息:Must be one of: Context. POWER_SERVICE ...
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
alexhilton5 天前
Compose中初始加载逻辑究竟应该放在哪里?
android·kotlin·android jetpack
我命由我123455 天前
Android 开发问题:SharedPreferences 的 getString 方法返回值类型 Type mismatch 问题
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
我命由我123456 天前
Android 开发中,关于 Gradle 的 distributionUrl 的一些问题
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
撩得Android一次心动6 天前
Android DataBinding 全面解析【源码篇1】
android·android jetpack·databinding
阿巴斯甜7 天前
Hilt 的其它对象注入到普通类和普通类( @Inject constructor)注入到其它类(Activity或Fragment)
android jetpack