Android开发-SharedPreferences
一、SharedPreferences 是什么?
SharedPreferences
是 Android 提供的一个轻量级的键值对(Key-Value)存储系统,基于 XML 文件实现,通常用于存储:
- ✅ 用户偏好设置(如主题、语言)
- ✅ 应用配置项(如是否开启新手引导)
- ✅ 登录状态(如 token、用户ID)
- ✅ 小型缓存数据(如最后更新时间)
⚠️ 注意:它不适合存储大量数据或复杂对象(如用户列表、聊天记录)。
二、基本使用:存取数据
1. 获取 SharedPreferences 实例
// 方式1:使用默认文件名(包名 + _preferences)
SharedPreferences sp = getSharedPreferences("user_prefs", Context.MODE_PRIVATE);// 方式2:使用默认偏好文件(推荐用于全局设置)
SharedPreferences defaultSp = PreferenceManager.getDefaultSharedPreferences(this);
🔑
Context.MODE_PRIVATE
:文件仅对本应用可读写,是唯一推荐的模式。
2. 写入数据(使用 Editor)
SharedPreferences.Editor editor = sp.edit();editor.putString("username", "张三");
editor.putInt("age", 25);
editor.putBoolean("is_dark_mode", true);
editor.putFloat("volume", 0.8f);
editor.putLong("last_login_time", System.currentTimeMillis());// 提交更改
boolean isSuccessful = editor.commit(); // 同步提交,返回是否成功
// 或者
editor.apply(); // 异步提交,无返回值,推荐使用
✅
apply()
vscommit()
:
apply()
:异步写入内存,再异步写入磁盘,无阻塞,推荐使用。commit()
:同步写入磁盘,会阻塞主线程,仅在需要确认写入结果时使用。
3. 读取数据
String username = sp.getString("username", "默认用户名");
int age = sp.getInt("age", 0);
boolean isDarkMode = sp.getBoolean("is_dark_mode", false);
long lastLogin = sp.getLong("last_login_time", 0L);
🔐 第二个参数是默认值,当键不存在时返回。
三、核心原理与线程安全
1. 内部机制
- 数据存储在
/data/data/<package_name>/shared_prefs/
目录下的 XML 文件中。 apply()
会先将更改写入内存中的Map
,然后在一个后台线程中异步写入磁盘。commit()
直接在当前线程同步写入磁盘。
2. 线程安全吗?
✅ 是线程安全的,但需注意:
- 读写操作:
SharedPreferences
对象本身是线程安全的。 - Editor 对象:不是线程安全的!不要在多个线程中共享同一个
Editor
实例。
// ❌ 错误做法
SharedPreferences.Editor editor = sp.edit();
new Thread(() -> editor.putString("key1", "value1")).start();
new Thread(() -> editor.putString("key2", "value2")).start(); // 可能覆盖或丢失数据// ✅ 正确做法:每个线程获取自己的 Editor
new Thread(() -> {SharedPreferences.Editor e1 = sp.edit();e1.putString("key1", "value1");e1.apply();
}).start();new Thread(() -> {SharedPreferences.Editor e2 = sp.edit();e2.putString("key2", "value2");e2.apply();
}).start();
四、监听数据变化
使用 OnSharedPreferenceChangeListener
监听特定键值的变化。
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);SharedPreferences.OnSharedPreferenceChangeListener listener = new SharedPreferences.OnSharedPreferenceChangeListener() {@Overridepublic void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {if ("is_dark_mode".equals(key)) {boolean isDark = sharedPreferences.getBoolean(key, false);updateTheme(isDark);}}};// 注册监听器
sp.registerOnSharedPreferenceChangeListener(listener);// 在 Activity/Fragment 销毁时务必注销!
@Override
protected void onDestroy() {super.onDestroy();sp.unregisterOnSharedPreferenceChangeListener(listener); // 防止内存泄漏
}
五、最佳实践与注意事项
1. 使用常量定义键名
避免魔法字符串,提高可维护性。
public class PrefKeys {public static final String KEY_USERNAME = "username";public static final String KEY_AGE = "age";public static final String KEY_DARK_MODE = "is_dark_mode";// ...
}
2. 避免存储敏感信息
- 不要直接存储密码、token 等敏感信息。
- 建议:使用
Android Keystore
系统或加密后存储。
3. 不要存储复杂对象
- ❌ 避免将
Object
直接toString()
存储。 - ✅ 正确做法:
- 使用
Gson
将对象序列化为 JSON 字符串。 - 使用
Room
数据库存储复杂数据。
- 使用
// 使用 Gson 存储对象
Gson gson = new Gson();
User user = new User("张三", 25);
String userJson = gson.toJson(user);
editor.putString("user_info", userJson);
editor.apply();// 读取
String userJson = sp.getString("user_info", null);
if (userJson != null) {User user = gson.fromJson(userJson, User.class);
}
4. 注意性能
- 避免频繁写入:
apply()
虽然异步,但频繁调用仍可能影响性能。 - 批量操作:使用同一个
Editor
批量写入多个值。
SharedPreferences.Editor editor = sp.edit();
editor.putString("key1", "value1");
editor.putString("key2", "value2");
editor.putString("key3", "value3");
editor.apply(); // 一次提交
5. 文件大小限制
- 单个
SharedPreferences
文件不宜过大(建议 < 100KB)。 - 过大时考虑拆分文件或使用数据库。
6. 初始化与默认值
- 在应用启动时,确保关键配置有合理的默认值。
- 可在
Application
类中初始化。
六、替代方案对比
存储方案 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
SharedPreferences | 键值对、小数据、配置 | 简单、快速、轻量 | 不适合复杂数据、无查询能力 |
Internal Storage | 私有文件 | 完全控制 | 需手动管理格式 |
External Storage | 公开文件(如图片) | 用户可访问 | 权限、兼容性问题 |
SQLite / Room | 结构化数据、大量数据 | 强大、支持查询、事务 | 复杂、开销大 |
DataStore | 替代 SP(Jetpack) | 支持协程、类型安全、无 XML | 较新,需学习 |
🚀 未来趋势:Google 推荐使用
Jetpack DataStore
逐步替代SharedPreferences
,它基于Kotlin Coroutines
和Flow
,提供更现代、安全的 API。
七、动手实践:构建一个设置管理器
public class SettingsManager {private static final String PREF_NAME = "app_settings";private SharedPreferences sp;public SettingsManager(Context context) {sp = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);}public void setUsername(String username) {sp.edit().putString(PrefKeys.KEY_USERNAME, username).apply();}public String getUsername() {return sp.getString(PrefKeys.KEY_USERNAME, "");}public void setDarkMode(boolean enabled) {sp.edit().putBoolean(PrefKeys.KEY_DARK_MODE, enabled).apply();}public boolean isDarkMode() {return sp.getBoolean(PrefKeys.KEY_DARK_MODE, false);}// ... 其他设置
}
八、总结:SharedPreferences 使用 Checklist
项目 | 是否完成 |
---|---|
✅ 使用 apply() 而非 commit() | ☐ |
✅ 定义常量存储键名 | ☐ |
✅ 在适当位置注销监听器 | ☐ |
✅ 避免存储敏感信息 | ☐ |
✅ 不存储复杂对象(或使用序列化) | ☐ |
✅ 控制文件大小 | ☐ |
✅ 提供合理的默认值 | ☐ |
✅ 考虑未来迁移到 DataStore | ☐ |
九、结语
感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!