剖析 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,保证数据一致性,不会造成数据损坏
  • 可以监听到操作成功或者失败结果

SharedPreferences性能问题

 

相关文章: