Android 入门教程
SharedPreferences
本教程共 100 篇 · 第 65 篇 · 更新于 2026-07-28 · 约 8 分钟阅读
AndroidAndroid 入门教程SharedPreferencesDataStore键值对存储偏好设置
65. SharedPreferences
本节目标:学会用 SharedPreferences 读写键值对数据,理解 commit 与 apply 的区别,了解 DataStore 作为现代替代方案。
SharedPreferences 是什么
SharedPreferences(简称 SP)是 Android 的轻量级键值对存储方案。适合存少量简单数据,比如用户设置、登录标志、主题偏好。
打个比方:SP 像一张便签纸,你记几个关键配置(「深色模式:开」「语言:中文」),下次打开应用时直接读。不适合记大量或复杂的数据。
获取 SharedPreferences 实例
两种方式:
// 方式一:指定文件名,整个应用共享
val sp = context.getSharedPreferences("user_settings", Context.MODE_PRIVATE)
// 方式二:Activity 专属,不需要文件名
val sp = getPreferences(Context.MODE_PRIVATE)
Note
MODE_PRIVATE表示只有本应用能访问。早期还有MODE_WORLD_READABLE/MODE_WORLD_WRITABLE让其他应用访问,但已废弃,现在只能用MODE_PRIVATE。文件名建议用应用包名做前缀,如com.example.app.settings。
写入数据
val sp = getSharedPreferences("settings", Context.MODE_PRIVATE)
val editor = sp.edit()
editor.putString("username", "张三")
editor.putInt("age", 25)
editor.putBoolean("darkMode", true)
editor.putFloat("score", 95.5f)
editor.putLong("loginTime", System.currentTimeMillis())
// 提交保存
editor.apply()
KTX 简化写法:
sp.edit {
putString("username", "张三")
putBoolean("darkMode", true)
}
读取数据
val username = sp.getString("username", "默认值")
val age = sp.getInt("age", 0)
val darkMode = sp.getBoolean("darkMode", false)
每个 getXxx 方法第二个参数是默认值:如果 key 不存在,返回这个值。
commit vs apply
提交保存有两种方式:
| 方法 | 执行方式 | 返回值 | 适用场景 |
|---|---|---|---|
apply() | 异步,后台写文件 | 无 | 日常使用(推荐) |
commit() | 同步,阻塞当前线程 | Boolean(是否成功) | 需要确认写入成功时 |
// 推荐:异步,不阻塞 UI
editor.apply()
// 同步:阻塞直到写完,但能知道是否成功
val success = editor.commit()
Warning
- 日常用
apply(),它是异步的不阻塞 UI 线程。commit()同步写文件会卡 UI,除非你需要立刻确认保存成功(比如关键数据写入前),否则别用。但apply()的缺点是你不知道是否保存成功,而且系统在apply()后立刻关机可能丢失数据。
删除数据
sp.edit {
remove("username") // 删除单个 key
// clear() // 清空所有数据
}
判断 key 是否存在
if (sp.contains("username")) {
// 存在
}
注册数据变化监听
val listener = SharedPreferences.OnSharedPreferenceChangeListener { sp, key ->
when (key) {
"darkMode" -> {
val darkMode = sp.getBoolean(key, false)
// 主题变了,更新界面
}
}
}
sp.registerOnSharedPreferenceChangeListener(listener)
// 不用时注销
sp.unregisterOnSharedPreferenceChangeListener(listener)
Tip
- 监听器持有 SharedPreferences 的弱引用,不会导致内存泄漏。但 Activity 销毁时仍建议注销,保持代码整洁。
SP 的局限
SharedPreferences 有几个问题:
- 同步读阻塞:
getXxx()是同步的,首次加载时在主线程读 XML 文件。 - 不支持大数据:存几百个键值对以上就慢了。
- 类型不安全:
putInt存的值用getString读会崩。 - 不支持协程:无法用协程等待写入完成。
- 数据可见性:SP 存的是 XML 明文文件,root 设备上可被读取。
DataStore(现代替代方案)
官方推荐用 Jetpack DataStore 替代 SharedPreferences。它解决了 SP 的上述问题:协程支持、异步非阻塞、类型安全。
添加依赖
dependencies {
implementation("androidx.datastore:datastore-preferences:1.1.1")
}
Preferences DataStore
// 创建 DataStore
private val Context.dataStore by preferencesDataStore(name = "settings")
// 定义 key
val DARK_MODE = booleanPreferencesKey("dark_mode")
val USERNAME = stringPreferencesKey("username")
// 写入(suspend 函数,在协程里调)
suspend fun saveSettings(username: String, darkMode: Boolean) {
context.dataStore.edit { preferences ->
preferences[USERNAME] = username
preferences[DARK_MODE] = darkMode
}
}
// 读取(返回 Flow,可观察)
val darkModeFlow: Flow<Boolean> = context.dataStore.data
.map { preferences -> preferences[DARK_MODE] ?: false }
在 Compose 中使用
@Composable
fun SettingsScreen() {
val context = LocalContext.current
val scope = rememberCoroutineScope()
// 读:collectAsState 自动响应变化
val darkMode by context.dataStore.data
.map { it[DARK_MODE] ?: false }
.collectAsState(initial = false)
// 写:在协程里
Switch(
checked = darkMode,
onCheckedChange = { value ->
scope.launch {
context.dataStore.edit { it[DARK_MODE] = value }
}
}
)
}
Note
- DataStore 有两种:
Preferences DataStore(类似 SP,不预定义 schema)和Proto DataStore(用 Protocol Buffers,类型安全更强但需要定义 schema)。日常用 Preferences DataStore 就够了,API 和 SP 类似但更现代。
SP vs DataStore 对比
| 特性 | SharedPreferences | DataStore |
|---|---|---|
| 读写方式 | 同步读,异步/同步写 | 全异步(协程/Flow) |
| 线程安全 | 不保证 | 保证 |
| 类型安全 | 不安全 | 安全 |
| 数据观察 | 监听器回调 | Flow 响应式 |
| 推荐度 | 老项目可用 | 新项目推荐 |
Tip
- 新项目直接用 DataStore。老项目如果 SP 用得不多,迁移成本不高,也建议换。SP 不是不能用,但 DataStore 在各方面都更好,特别是和协程、Compose 配合时。
小结
SharedPreferences 适合存少量键值对配置,apply() 异步写入是日常首选。但它有同步阻塞、类型不安全等局限。新项目推荐用 DataStore:全异步、协程支持、Flow 响应式,和 Compose 配合更自然。下一节学习文件存储。