SharedPreferences是Android中的键值数据存储,用于保存简单的应用程序设置和配置。数据以XML文件形式存储在设备上,只能在创建它的应用程序内部访问。根据官方文档 Android Developers, 2025,SharedPreferences支持存储原始类型:String、Int、Boolean、Float、Long和Set<String>。这是保存少量用户设置的最简单、最快的解决方案,无需SQL查询或直接与文件系统交互。
要点
SharedPreferences是Android内置的机制,用于在设备内部存储的XML文件中保存键值对。它从API Level 1开始可用,不需要连接额外的库。主要用途是保存用户设置、界面状态、首次启动标志以及其他不需要结构化数据库的简单数据。
每个SharedPreferences文件都与特定的名称和访问模式相关联。默认使用Context.MODE_PRIVATE模式,该模式将文件访问限制为仅当前应用程序。以前Android支持MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE模式,但从API Level 17开始它们被声明为已弃用,并在Android 7.0(API 24)中出于安全原因被完全删除。
尽管简单,SharedPreferences被数百万Android应用程序使用。根据Google的数据,Google Play上发布的应用程序中超过90%使用SharedPreferences来存储设置。然而,对于复杂场景(大量数据、类型安全、异步),Google推荐更现代的解决方案,例如来自Android Jetpack库的Preferences DataStore。
物理上,SharedPreferences以XML文件形式存储在应用程序目录中:/data/data/{package_name}/shared_prefs/{file_name}.xml。文件包含根元素<map>以及根据存储值类型而定的子元素<string>、<int>、<boolean>、<float>和<long>。文件大小没有限制,但对于大量数据(超过100 KB),读写性能开始显著下降。
SharedPreferences文件默认不加密。数据以明文形式存储在设备的文件系统中。对于存储敏感数据(令牌、密码),建议使用AndroidX Security库中的EncryptedSharedPreferences,它会使用AES256-GCM自动加密键和值。
SharedPreferences基于内存缓存并定期同步到磁盘的原理工作。首次访问文件时(通过getSharedPreferences),Android将XML文件加载到RAM中并将其解析为Map对象。所有后续读取操作都从内存中执行,无需重新从磁盘读取。这确保了数据的高速访问。
写入操作使用Editor — 一个内部更改缓冲区。当开发者调用putString或putBoolean时,更改将保存在内存中的Editor对象中。实际的磁盘写入发生在调用commit(同步)或apply(异步)方法时。在调用这些方法之前,数据不会被保存,如果应用程序意外终止,更改可能会丢失。
获取SharedPreferences实例使用两种方法:getPreferences和getSharedPreferences。第一种仅在Activity内部可用,并创建以Activity命名的文件。第二种更灵活,接受文件名和访问模式,并可从任何上下文(Application、Activity、Service)访问。建议使用getSharedPreferences,文件名与应用程序的模块或功能相对应。
// 获取SharedPreferences
val prefs = context.getSharedPreferences(
"user_settings", Context.MODE_PRIVATE
)
// 写入数据
with(prefs.edit()) {
putString("username", "安娜")
putInt("age", 28)
putBoolean("isLoggedIn", true)
apply()
}
// 读取数据
val username = prefs.getString("username", "")
val age = prefs.getInt("age", 0)
val isLoggedIn = prefs.getBoolean("isLoggedIn", false)
使用MODE_MULTI_PROCESS(已弃用)时,SharedPreferences会在进程之间同步。然而,这种同步不保证原子性,Google建议避免在multi-process场景中使用SharedPreferences。对于这种情况,最好使用ContentProvider、具有进程间访问的Room或DataStore。
SharedPreferences提供了一组按键读取数据的方法和用于写入的Editor接口。每个读取方法接受两个参数:键和默认值(如果未找到键则返回)。默认值也决定了返回值的类型:getString返回String,getInt返回Int,依此类推。
| 读取方法 | 写入方法 | 数据类型 |
|---|---|---|
| getString | putString | String |
| getInt | putInt | Int |
| getBoolean | putBoolean | Boolean |
| getFloat | putFloat | Float |
| getLong | putLong | Long |
| getStringSet | putStringSet | Set<String> |
Editor — SharedPreferences的内部对象,在缓冲区中收集更改。在进行所有更改后,开发者调用commit()(同步写入)或apply()(异步写入)。区别至关重要:commit阻塞当前线程直到完全写入磁盘并返回boolean(成功/失败),而apply在后台线程中执行写入并立即返回控制权,但不返回结果。
在所有不需要知道写入结果的情况下,建议使用apply而不是commit。apply更快且不会阻塞UI线程。commit仅在需要知道数据是否成功保存或在使用multi-process模式时才应使用。对于删除单个键,使用remove方法;对于完全清除,使用clear。所有删除操作也通过Editor执行。
// 多次更改 - 一次apply
prefs.edit {
putString("theme", "dark")
putBoolean("notifications", false)
remove("old_key")
}
// 值更改监听器
prefs.registerOnSharedPreferenceChangeListener { prefs, key ->
Log.d("TAG", "键已更改:$key")
}
从Android 12(API 31)开始,SharedPreferences增加了对registerOnSharedPreferenceChangeListener的支持,通过Lifecycle自动取消注册。这有助于避免与遗忘的监听器相关的内存泄漏。在较旧版本中,开发者必须在组件的onDestroy或onStop中手动调用unregisterOnSharedPreferenceChangeListener。
尽管广泛使用,SharedPreferences并不是Android中所有数据存储场景的通用解决方案。根据数据量、类型安全和性能要求,Google推荐包含在Android Jetpack和标准Android库中的各种替代方案。
| 解决方案 | 何时使用 | 缺点 |
|---|---|---|
| SharedPreferences | 小型设置(最多100个键) | 没有类型安全,同步读取 |
| DataStore | 中等复杂度的设置,使用协程 | 不支持API 14以下版本 |
| Room | 结构化数据和列表 | 对于3-5个设置来说过于复杂 |
| EncryptedSharedPreferences | 敏感数据和令牌 | 依赖AndroidX Security |
DataStore — 一个Android Jetpack库,由Google作为SharedPreferences的替代品推出。它提供两种变体:Preferences DataStore(键值,如SharedPreferences)和Proto DataStore(通过Protocol Buffers的类型化存储)。DataStore使用协程和Flow进行异步工作,保证类型安全,并自动处理版本迁移。Google推荐所有新项目使用DataStore。
DataStore的主要优势是API级别的异步性。所有读取操作都返回Flow,写入操作是suspend函数。这完全消除了SharedPreferences同步读取时可能出现的UI线程阻塞。此外,DataStore保证数据一致性:写入在事务中执行,如果失败,所有更改都将回滚。
让我们看一个实际示例:Android应用程序中的主题设置(亮色/暗色/系统)。用户选择主题,选择保存在SharedPreferences中。在后续应用程序启动时,主题从保存的设置中恢复。对于响应式界面更新,通过SharedPreferences.OnSharedPreferenceChangeListener观察更改。
我们将创建ThemePreferences类,它封装了与SharedPreferences相关的所有主题工作。该类提供getTheme(读取)、setTheme(写入)和observeTheme(观察)方法。设置文件名称为"app_preferences",使用MODE_PRIVATE模式。为方便起见,键作为常量放在companion object中。
class ThemePreferences(context: Context) {
companion object {
private const val PREF_NAME = "app_preferences"
private const val KEY_THEME = "theme_mode"
const val THEME_LIGHT = "light"
const val THEME_DARK = "dark"
const val THEME_SYSTEM = "system"
}
private val prefs = context
.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
fun getTheme(): String =
prefs.getString(KEY_THEME, THEME_SYSTEM) ?: THEME_SYSTEM
fun setTheme(theme: String) {
prefs.edit { putString(KEY_THEME, theme) }
}
fun observeTheme(callback: (String) -> Unit) {
prefs.registerOnSharedPreferenceChangeListener { _, key ->
if (key == KEY_THEME) {
callback.invoke(getTheme())
}
}
}
}
在Activity或Fragment中,通过应用程序上下文获取ThemePreferences实例。初始化时调用getTheme设置当前主题。当用户选择新主题时,调用setTheme,通过observeTheme界面无需重启Activity即可更新。重要的是不要忘记在onDestroy中取消注册监听器以防止内存泄漏,特别是在配置更改时重新创建Activity的情况下。
对于最低目标版本为Android 12+的应用程序,建议使用registerOnSharedPreferenceChangeListener与LifecycleObserver结合。这会在组件生命周期变化时自动管理注册和取消注册。对于较旧版本,注册和取消注册必须手动管理,这是使用SharedPreferences的生产应用程序中常见的错误来源。
常见问题
SharedPreferences直接只支持原始类型和Set<String>。要存储对象,需要通过Gson或Moshi将它们序列化为JSON字符串,通过putString保存,并在读取时反序列化。对于具有大量字段的复杂对象,建议使用Room而不是带有JSON序列化的SharedPreferences。
是的,SharedPreferences是线程安全的。所有读写操作都在SharedPreferences对象及其Editor级别同步。然而,在使用multi-process模式时,不保证同步。对于单个应用程序中来自多个线程的并发访问,SharedPreferences在没有额外锁的情况下是安全的。
要完全清除SharedPreferences中的所有数据,在Editor上调用clear()方法并通过apply应用更改。如果需要删除XML文件本身,在上下文上使用deleteSharedPreferences(name)。通过设置→应用程序→清除数据来清除应用程序数据也会删除所有SharedPreferences文件。
对于新项目,Google推荐DataStore作为SharedPreferences的替代品。DataStore提供使用协程的异步工作、类型安全(Proto DataStore)和自动迁移。只有在最低版本低于API 14的项目或需要快速集成且无需额外依赖时,才应选择SharedPreferences。
要加密数据,请使用AndroidX Security库中的EncryptedSharedPreferences。它使用AES-256 GCM自动加密键和值。设置过程非常简单:将getSharedPreferences替换为EncryptedSharedPreferences.create,并指定来自Android Keystore的主密钥。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。