为什么 Google 不再推荐 SharedPreferences?答案其实只有一个:锁

前言

在很多 Android 项目中,配置存储一直是这样写的:

简单、直接,几乎人人都在用。

但在 Jetpack 体系中,Google 明确建议:

新项目应优先使用 DataStore,而不是 SharedPreferences。

于是网上出现了大量"DataStore 好在哪里"的文章,答案千篇一律:

  • ✅ 支持 Flow
  • ✅ 支持协程
  • ✅ 类型更安全

这些当然都对。但如果只停留在这一层,你其实还没真正理解 Google 为什么要重新设计本地存储。

从并发架构角度看,本质区别只有一句话:

SharedPreferences 是「锁模型」,DataStore 是「无锁消息模型」。


一、SharedPreferences 的并发模型:锁

SharedPreferences 的核心实现类是 SharedPreferencesImpl,内部有两把关键锁:

分别负责:

  • 内存 Map 的访问控制
  • 磁盘写入的互斥保护

读取时:

写入时:

结论很清晰:

SharedPreferences 的并发安全,完全依赖锁来保证。

线程模型如下:

css 复制代码
线程A ──┐
线程B ──┤──▶ 竞争锁 ──▶ 访问 SharedPreferences
线程C ──┘

二、锁 + 磁盘 IO = 定时炸弹

锁本身不是问题,真正的问题是:

锁 + 磁盘 IO 同时出现。

SharedPreferences 的数据以 XML 文件存储:

kotlin 复制代码
/data/data/<package>/shared_prefs/*.xml

当线程 A 正在写 XML,线程 B(主线程)想读取时,它必须等待 mLock 释放。

主线程一旦被阻塞:

复制代码
UI 卡顿 → 极端情况下触发 ANR

三、apply() 也救不了你

很多人以为用 apply() 代替 commit() 就万事大吉了。

其实并没有。

apply() 的写磁盘操作确实是异步的,但 Android 系统在以下节点会强制等待所有 pending 任务完成:

commit() 的 ANR 发生在调用处,直接、明显; apply() 的 ANR 发生在 onStop(),隐蔽,难排查。

commit() apply()
阻塞时机 调用处 onStop()
ANR 风险 明显 隐蔽

这也是 DataStore 用协程彻底解决这个问题的核心动机之一。

四、为什么 Google 不继续优化 SharedPreferences?

理论上可以改进锁策略:ReadWriteLock、CAS、更细粒度的锁......

但有一个根本问题无法绕过:

SharedPreferences 使用 XML 文件,而 XML 必须整体读取、整体写入。

这意味着锁的粒度几乎无法优化。局部修改不可能,并发写入不安全,架构层面已经走到了天花板。


五、DataStore:换一种思路------Actor 模型

Jetpack DataStore 采用了完全不同的并发模型:

css 复制代码
线程A ──┐
线程B ──┤──▶ 发送消息 ──▶ Channel ──▶ 单协程顺序处理 ──▶ 写入磁盘
线程C ──┘

这就是 Actor 模型,核心思想是:

不共享状态,只通过消息通信。

updateData {} 本质上就是往 Channel 里发一条消息,由一个单独的协程负责顺序处理所有读写操作。

因为只有一个执行者,根本不需要锁。


六、Actor 模型带来的连锁优势

新的并发基础之上,DataStore 自然获得了一系列能力:

① 协程 IO,不阻塞主线程

所有磁盘操作都是 suspend 函数,彻底告别主线程 IO。

② 原子事务,不会部分写入

③ Flow 响应式,天然融入现代架构

无缝对接 Compose、MVI、响应式架构。

七、一张表说清楚

SharedPreferences DataStore
并发模型 锁模型 Actor 消息模型
IO 方式 同步 / 伪异步 纯异步(协程)
原子性 ❌ ✅
ANR 风险 有,且隐蔽 无
响应式支持 ❌ ✅ Flow
类型安全 ❌ ✅(Proto)

总结

SharedPreferences 诞生于 Android 早期------那时多线程简单,数据规模小,XML 够用。

但今天,协程、Flow、Compose、响应式架构已经是主流。在这个背景下,「锁模型」逐渐被「消息模型」取代,是架构演进的必然结果。

表面是 API 的一次升级,本质是并发模型的彻底重构------读懂这一点,才算真正理解了 DataStore。

相关推荐
敲代码的瓦龙24 分钟前
Jetpack?lifecycle!!!
android·kotlin
苦瓜花30 分钟前
Hilt用使用及原理
android
深度学习04072 小时前
MySQL InnoDB Cluster 部署完整文档
android·mysql·adb
邪修king2 小时前
Re:Linux系统篇(三十七):进程间通信Chapter4:System V 共享内存深度解析:为什么它是最快的 IPC?
android·linux·运维
用户337922545683 小时前
Kiora SDK:在 Android 上组织端侧模型、工具与 A2A 协作
android
洞见前行3 小时前
你在 XML 时代学的渲染优化,进了 Compose 大多不再是重点——Recomposition 才是新的战场
android
ii_best3 小时前
按键精灵手机助手 v5.2.1 实测:inputText 逐字延时 + 调试面板多行复制,安卓脚本开发的两个输入痛点一次解决
android·开发语言·智能手机·自动化·ai编程·按键精灵
hunterandroid3 小时前
OkHttp 源码解析:拦截器链、连接池与缓存策略的设计精髓
android
蒲公英内测分发3 小时前
智能手环出海测试,海外合作方分散在不同国家,Android 测试包怎么持续分发和更新?
android·前端·人工智能·测试工具·智能手表
朱涛的自习室4 小时前
高价软件已死,我让 AI 开启了「平替计划」(Day 7 / 7)
android·前端·github