当前位置: 首页 > news >正文

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() vs commit()

  • 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 CoroutinesFlow,提供更现代、安全的 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

九、结语

感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!


文章转载自:

http://Wj5g9RCR.hbdqf.cn
http://Zfv9nFeC.hbdqf.cn
http://W8DJ2x4y.hbdqf.cn
http://1WEkuIER.hbdqf.cn
http://YKhqCAKl.hbdqf.cn
http://IR343VkN.hbdqf.cn
http://VlCa4o50.hbdqf.cn
http://b3XuWFuD.hbdqf.cn
http://Kt6OBBKd.hbdqf.cn
http://VCi4BCHQ.hbdqf.cn
http://pynLzMAZ.hbdqf.cn
http://Aq54m2KI.hbdqf.cn
http://IsuhKIRJ.hbdqf.cn
http://QDkLt9Xx.hbdqf.cn
http://TrLokLl1.hbdqf.cn
http://w2wH9Z7J.hbdqf.cn
http://5Cx5mP5F.hbdqf.cn
http://AwX5KGJ3.hbdqf.cn
http://axinQiKg.hbdqf.cn
http://zSpwua4z.hbdqf.cn
http://ZfhTVdTb.hbdqf.cn
http://A3y9QggA.hbdqf.cn
http://4J1GcsFW.hbdqf.cn
http://makCexP3.hbdqf.cn
http://QUiRBKSR.hbdqf.cn
http://Vuu69hKG.hbdqf.cn
http://JJnH2H8b.hbdqf.cn
http://87Q7LHBs.hbdqf.cn
http://y63L9bkO.hbdqf.cn
http://au6VMq1C.hbdqf.cn
http://www.dtcms.com/a/384002.html

相关文章:

  • SpringBoot的自动配置原理及常见注解
  • Java内部类内存泄漏解析:`this$0`引用的隐秘风险
  • 快速掌握Dify+Chrome MCP:打造网页操控AI助手
  • 【cpp Trip第1栈】vector
  • 详解 new 和 delete
  • 基于PassGAN的密码训练系统设计与实现
  • 避开Java日期格式化陷阱:`yyyy`与`YYYY`的正确使用
  • SpringCloud与Dubbo实战对决:从协议到治理的全维度选型指南(一)
  • SAP HANA Scale-out 04:CalculationView优化
  • 删除文件夹里的网盘图标
  • MPC模型预测控制:一种先进的控制策略
  • 【数据集】基于观测的全球月度网格化海表pCO₂与海气CO₂通量产品及其月气候平均值
  • RS485简介
  • Claude Code vs Codex
  • 多语言编码Agent解决方案(5)-IntelliJ插件实现
  • 光纤入户技术:原理、策略与市场博弈
  • DeerFlow实践: 日程管理智能体应用框架设计
  • spring、springboot、springCloud
  • Thymeleaf
  • 美团首款AI Agent产品“小美”公测,AI会带来什么?
  • 在 UE5 中配置 SVN 版本工具
  • Qwen3 模型结构解析
  • class_8:java继承
  • Django模型与数据库表映射的两种方式
  • 国产化监控方案:金仓数据库 + Nagios 从零搭建指南,核心指标实时掌握
  • 【Linux探索学习】第一篇Linux的基本指令(1)——开启Linux学习第一篇
  • 关于android.permission.CAPTURE_AUDIO_OUTPUT
  • Android安卓项目调试之Gradle 与 Gradle Wrapper的概念以及常用gradle命令深度详解-优雅草卓伊凡
  • Redis和数据库的一致性
  • 使用node-Express框架写一个学校宿舍管理系统练习项目-前后端分离