剖析 SharedPreference apply 引起的 ANR 问题 https://mp.weixin.qq.com/s?__biz=MzI1MzYzMjE0MQ==&mid=2247484387&idx=1&sn=e3c8d6ef52520c51b5e07306d9750e70&scene=21#wechat_redirect
SharedPreferences性能问题 https://bytedance.feishu.cn/docs/doccnqs6lpMphwYg0R6cOZaW7Cc#
SharedPreferences ANR 总结 https://zhuanlan.zhihu.com/p/152623807
获取 SharedPreferences 总结:
- 获取 SP 的过程是通过 synchronized 关键字保证多线程安全的。
- 通过 Map 进行缓存 Sp 实例,因此多次调用 getSharedPreferences 几乎没有性能上的差别。
- 获取 Sp 的时候就会通过一个线程将 xml 数据从磁盘加载到内存中。这个过程会加锁,加载完成后会设置 mLoaded 标志,并唤醒其他线程。
getXXX 方法 总结:
- 通过 synchronized 进行线程安全保证
- 在主线程进行获取,但是需要等加载的完成后才能进行读,所以get 方法可能造成主线程阻塞,从而导致 ANR 。
- 加载完成后读的过程只涉及内存的读。
apply 总结
- apply 没有返回值
- apply 是在主线程将修改数据提交到内存, 然后再子线程(HandleThread)提交到磁盘
- apply 会将 Runnble 添加到 QueueWork 中,如果主线程 Activity 暂停时或者 BroadcastReceiver 的 onReceive 方法调用后或者 service 的命令处理,就会在主线程执行 提交到硬盘的方法,这个过程就会造成主线程 ANR
commit 总结
- commit 有返回值
- commit 是在主线程将修改数据提交到内存, 然后再在主线程提交到磁盘
- 用 commit 方法最保险。如果担心在主线程调用 commit 方法会出现 ANR,可以将所有的 commit 任务放到单线程池的线程里去执行。
总结
- Sp 主线程 getXX 方法会 ANR
- Sp apply 方法会 ANR
- Sp 主线程调用 commit 方法 ANR
[Google] 再见 SharedPreferences 拥抱 Jetpack DataStore https://juejin.im/post/6881442312560803853
SharedPreference 是一个轻量级的数据存储方式,使用起来也非常方便,以键值对的形式存储在本地,初始化 SharedPreference 的时候,会将整个文件内容加载内存中,因此会带来以下问题:
- 通过
getXXX()方法获取数据,可能会导致主线程阻塞 - SharedPreference 不能保证类型安全
- SharedPreference 加载的数据会一直留在内存中,浪费内存
-
apply()方法虽然是异步的,可能会发生 ANR,在 8.0 之前和 8.0 之后实现各不相同 -
apply()方法无法获取到操作成功或者失败的结果
另外还做了一个很重要的优化,当调用 apply() 方法的时候,执行磁盘写入,都是全量写入,在 8.0 之前,调用 N 次 apply() 方法,就会执行 N 次磁盘写入,在 8.0 之后,apply() 方法调用了多次,只会执行最后一次写入,通过版本号来控制的。
Preferences DataStore 相比于 SharedPreferences 优点
- DataStore 是基于 Flow 实现的,所以保证了在主线程的安全性
- 以事务方式处理更新数据,事务有四大特性(原子性、一致性、 隔离性、持久性)
- 没有
apply()和commit()等等数据持久的方法 - 自动完成 SharedPreferences 迁移到 DataStore,保证数据一致性,不会造成数据损坏
- 可以监听到操作成功或者失败结果