SharedPreferences
问题
SharedPreferences 的原理是什么?有哪些已知问题?
答案
基本原理
SharedPreferences 将数据以 XML 文件形式存储在 /data/data/<package>/shared_prefs/ 目录下:
已知问题
1. 全量写入
每次调用 apply() / commit() 都会将整个 Map 序列化为 XML 写入磁盘,数据量大时性能差。
2. apply 导致 ANR
apply() 虽然异步写磁盘,但 Activity onStop 时会在主线程等待所有 apply 任务完成:
// ❌ 高频调用 apply 可能导致 ANR
fun trackEvent(key: String, value: Int) {
prefs.edit().putInt(key, value).apply()
// onStop 时主线程会等待这些 apply 完成
}
3. 非类型安全
// ❌ key 写错或类型不匹配,编译期无法发现
val name = prefs.getString("user_name", "") // 运行时才发现问题
val age = prefs.getInt("user_age", 0)
4. 不支持跨进程
MODE_MULTI_PROCESS 已废弃,多进程读写 SP 不可靠。
commit vs apply
| 特性 | commit() | apply() |
|---|---|---|
| 执行方式 | 同步写磁盘 | 异步写磁盘 |
| 返回值 | boolean(是否成功) | void |
| 阻塞主线程 | ✅ 写入时阻塞 | ❌ 但 onStop 时会等待 |
| 推荐场景 | 需要确认写入成功 | 一般场景 |
为什么不推荐 SP
SP 的全量写入、ANR 风险和类型不安全问题使其不适合现代开发。Google 推荐使用 DataStore 替代。
常见面试问题
Q1: SP 的 apply 一定不会 ANR 吗?
答案:
不是。apply() 将写入任务提交到 QueuedWork,在 Activity.onStop() 和 Service.onStartCommand() 等生命周期节点,主线程会调用 QueuedWork.waitToFinish() 等待所有 pending 的 apply 完成。如果有大量或大数据量的 apply 任务积压,就会在主线程阻塞导致 ANR。
Q2: SP 第一次读取为什么可能卡顿?
答案:
首次调用 getSharedPreferences() 时,会在子线程加载 XML 文件到内存。但如果在文件加载完成前就调用 getString() 等读取方法,主线程会被 await() 阻塞,等待文件加载完成。文件越大,阻塞时间越长。