SharedPreferences 想必都知道吧,是所有安卓人学数据持久化的入门必修课,它可以通过简单的键值对 (Key-Value) 方式来轻松存储和获取各种基本数据类型,在开发中常用于存储简单的应用配置、登录状态和用户偏好等等。
但它也有许多令人诟病的,比如无论 edit 还是 commit 都有可能引发 ANR ,容易阻塞线程,类型不安全,数据一致性难保等等。
反正我目前是还没遇到过
所以都 2026 了还有人在用 SP 吗?
官方:Welcome Jetpack DataStore, now in alpha - a new and improved data storage solution aimed at replacing SharedPreferences.
??????
介绍 DataStore 是个什么❓
依旧美美照搬
Jetpack DataStore 是一种数据存储解决方案,让您可以使用协议缓冲区 存储键值对或类型化对象。DataStore 使用 Kotlin 协程和 Flow 以异步、一致的事务方式存储数据。
DataStore 的分类 DataStore 分为两种 ,如下:
Preferences DataStore : 嗯… 和 SP 类似,就把它当成 SP 改良版吧,它可以用于存储简单的键值对 (Key-Value),不需要预定义架构,也不确保类型安全。
Proto DataStore : 用于存储类型化对象 ,用于存储自定义的强类型对象,使用ProtoBuf 来定义架构,确保类型安全 (本文暂不对它展开介绍)。
优势在我:
因为是基于协程 的,所以完全异步 ,主线程安全
DataStore 使用 Flow 暴露数据,支持响应式
事务性 更新,原子操作
支持 try-catch
……
添加依赖 这个不必多说,在 build.gradle 引入依赖。
1 implementation("androidx.datastore:datastore-preferences:1.2.1" )
创建DataStore 可以通过扩展函数 和属性委托 preferencesDataStore 来创建 DataStore<Preferences> 实例。把它放在文件顶层,以保证全局单例 。
1 val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "XXX" )
定义Keys 与 SP 不同直接传字符串不一样,DataStore 需要使用专门的函数来定义 Key 。
1 2 3 4 object PrefsKeys { val DARK_MODE = booleanPreferencesKey("dark_mode" ) val USERNAME = stringPreferencesKey("username" ) val AGE = intPreferencesKey("age" ) }
读取数据 定义好 key 后,就能用 DataStore.data 读取数据啦,它会返回一个 Flow<T> 对象,数据变化时会重新发出。
1 2 3 val userNameFlow: Flow<String> = context.dataStore.data .map { preferences -> preferences[USER_NAME_KEY] ?: "访客" }
写入数据 写入时使用的是 DataStore.edit 它是 上吊 挂起 的,所以必须在协程作用域 中执行。
1 2 3 4 5 suspend fun saveUserName ( name: String ) { context.dataStore.edit { settings -> settings[PrefsKeys.USERNAME] = name settings } }
迁移 如果老项目需要迁移,构建时传入 SharedPreferencesMigration 即可。
温馨提示:新旧请key名保持一致
1 2 3 4 5 6 val Context.dataStore by preferencesDataStore( name = "new" , produceMigrations = { context -> listOf(SharedPreferencesMigration(context,"old" )) } )
……
嗯…好麻烦,还是回去继续接着开心使用SharedPreferences了!
示例 额,说的再多不如直接上代码运行(以下代码由 AI 生成并人工微调,可运行)
DataStoreManager 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 import android.content.Context import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.intPreferencesKey import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.preferencesDataStore import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "user_prefs" ) class DataStoreManager (private val context: Context) { val USER_NAME_KEY = stringPreferencesKey("user_name" ) val CLICK_COUNT_KEY = intPreferencesKey("click_count" ) } val userNameFlow: Flow<String> = context.dataStore.data .map { preferences -> preferences[USER_NAME_KEY] ?: "访客" } val clickCountFlow: Flow<Int > = context.dataStore.data .map { preferences -> preferences[CLICK_COUNT_KEY] ?: 0 } suspend fun saveUserName (name: String ) { context.dataStore.edit { preferences -> preferences[USER_NAME_KEY] = name } } suspend fun incrementClickCount () { context.dataStore.edit { preferences -> val currentCounterValue = preferences[CLICK_COUNT_KEY] ?: 0 preferences[CLICK_COUNT_KEY] = currentCounterValue + 1 } } }
ViewModel 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import android.app.Application import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.launch class MainViewModel (application: Application) : AndroidViewModel(application) { private val dataStoreManager = DataStoreManager(application) val userNameFlow = dataStoreManager.userNameFlow val clickCountFlow = dataStoreManager.clickCountFlow fun saveName (name: String ) { viewModelScope.launch { dataStoreManager.saveUserName(name) } } fun incrementCount () { viewModelScope.launch { dataStoreManager.incrementClickCount() } } }
Activity 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 import android.os.Bundleimport androidx.appcompat.app.AppCompatActivityimport android.widget.Buttonimport android.widget.EditTextimport android.widget.TextViewimport androidx.activity.viewModelsimport androidx.lifecycle.Lifecycleimport androidx.lifecycle.lifecycleScopeimport androidx.lifecycle.repeatOnLifecycleimport kotlinx.coroutines.launchclass MainActivity : AppCompatActivity () { private val viewModel: MainViewModel by viewModels() override fun onCreate (savedInstanceState: Bundle ?) { super .onCreate(savedInstanceState) setContentView(R.layout.activity_main) val tvGreeting = findViewById<TextView>(R.id.tvGreeting) val tvCounter = findViewById<TextView>(R.id.tvCounter) val etName = findViewById<EditText>(R.id.etName) val btnSaveName = findViewById<Button>(R.id.btnSaveName) val btnIncrement = findViewById<Button>(R.id.btnIncrement) btnSaveName.setOnClickListener { val name = etName.text.toString() if (name.isNotBlank()) { viewModel.saveName(name) etName.text.clear() } } btnIncrement.setOnClickListener { viewModel.incrementCount() } lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { launch { viewModel.userNameFlow.collect { name -> tvGreeting.text = "你好,$name !" } } launch { viewModel.clickCountFlow.collect { count -> tvCounter.text = "点击次数: $count " } } } } } }
XML 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 <?xml version="1.0" encoding="utf-8" ?> <LinearLayout xmlns:android ="http://schemas.android.com/apk/res/android" android:layout_width ="match_parent" android:layout_height ="match_parent" android:orientation ="vertical" android:padding ="24dp" android:fitsSystemWindows ="true" )> <TextView android:id ="@+id/tvGreeting" android:layout_width ="wrap_content" android:layout_height ="wrap_content" android:text ="你好,访客!" android:textSize ="24sp" android:textStyle ="bold" /> <TextView android:id ="@+id/tvCounter" android:layout_width ="wrap_content" android:layout_height ="wrap_content" android:text ="点击次数: 0" android:textSize ="18sp" android:layout_marginTop ="8dp" /> <EditText android:id ="@+id/etName" android:layout_width ="match_parent" android:layout_height ="wrap_content" android:hint ="输入你的名字" android:layout_marginTop ="32dp" /> <Button android:id ="@+id/btnSaveName" android:layout_width ="match_parent" android:layout_height ="wrap_content" android:text ="保存名字" /> <Button android:id ="@+id/btnIncrement" android:layout_width ="match_parent" android:layout_height ="wrap_content" android:text ="增加点击次数" android:layout_marginTop ="16dp" /> </LinearLayout >
附: 官方对比图
相关网址 官方介绍DataStore