Moosphan / Android-Daily-Interview

:pushpin:每工作日更新一道 Android 面试题,小聚成河,大聚成江,共勉之~
5.5k stars 779 forks source link

2019-03-28:SharedPreferences 是线程安全的吗?它的 commit 和 apply 方法有什么区别? #15

Open Moosphan opened 5 years ago

Fritz-Xu commented 5 years ago

context.getSharedPreferences()开始追踪的话,可以去到ContextImpl的getSharedPreferences(),最终发现SharedPreferencesImpl这个SharedPreferences的实现类,在代码中可以看到读写操作时都有大量的synchronized,因此它是线程安全的

Fritz-Xu commented 5 years ago

commit是同步写入,会返回执行结果,apply方法是异步写入,并不会返回执行结果;但是SharedPreferences文件的写入是全量写入,即使只是修改了其中一条key-value,也会执行全部的写入操作,因为SharedPreferences只能用于存储体积较小的数据,太大了就容易引发OOM,同时如果需要修改多条数据,必须使用Editor来一次性完成修改再提交

leon5458 commented 5 years ago

SharedPreferences 是线程安全的 进程不安全的, commit 是同步写入,apply是异步写入。

mosentest commented 5 years ago

SharedPreferences 是线程安全的 进程不安全的, commit 是同步写入有返回值,apply是异步写入。

StefanShan commented 5 years ago
  1. apply没有返回值而commit返回boolean表明修改是否提交成功
  2. apply是将修改数据原子提交到内存, 而后异步真正提交到硬件磁盘, 而commit是同步的提交到硬件磁盘,因此,在多个并发的提交commit的时候,他们会等待正在处理的commit保存到磁盘后在操作,从而降低了效率。而apply只是原子的提交到内容,后面有调用apply的函数的将会直接覆盖前面的内存数据,这样从一定程度上提高了很多效率。

由于在一个进程中,sharedPreference是单实例,一般不会出现并发冲突,如果对提交的结果不关心的话,建议使用apply,当然需要确保提交成功且有后续操作的话,还是需要用commit的。

Omooo commented 5 years ago

SP 是线程安全,非进程安全。commit 和 apply 的方法里面都一个 commitToMemory 方法,即把更新同步到内容。至于落地磁盘,commit 也并非完全同步,如果 commit 前有 apply 还未落盘,commit 会异步等待 apply 落盘之后在执行,内部通过一个计数器来判断。 详细见:https://github.com/Omooo/Android-Notes/blob/master/blogs/Android/SharedPreferences.md

RedDargon commented 5 years ago

1.SharePreferences是线程安全的 里面的方法有大量的synchronized来保障。
2.SharePreferences不是进程安全的 即使你用了MODE_MULTI_PROCESS 。 3.第一次getSharePreference会读取磁盘文件,异步读取,写入到内存中,后续的getSharePreference都是从内存中拿了。 4.第一次读取完毕之前 所有的get/set请求都会被卡住 等待读取完毕后再执行,所以第一次读取会有ANR风险。 5.所有的get都是从内存中读取。 6.提交都是 写入到内存和磁盘中 。apply跟commit的区别在于 apply 是内存同步 然后磁盘异步写入任务放到一个单线程队列中 等待调用。方法无返回 即void commit 内存同步 只不过要等待磁盘写入结束才返回 直接返回写入成功状态 true or false 7.从 Android N 开始, 不再支持 MODE_WORLD_READABLE & MODE_WORLD_WRITEABLE. 一旦指定, 会抛异常 。也不要用MODE_MULTI_PROCESS 迟早被放弃。 8.每次commit/apply都会把全部数据一次性写入到磁盘,即没有增量写入的概念 。 所以单个文件千万不要太大 否则会严重影响性能。 建议用微信的第三方MMKV来替代SharePreference

zhaoerlei1989 commented 5 years ago

1.SharePreferences是线程安全的 里面的方法有大量的synchronized来保障。 2.commit是同步写入,会返回执行结果,apply方法是异步写入,并不会返回执行结果; 3.SharePreferences不是进程安全的  4.SharedPreferences是以XML的格式以文件的方式自动保存的,在DDMS中的File Explorer中展开到/data/data/<package name>/shared_prefs下,可以看到一个叫做SETTING_Infos.xml的文件 总结比较不错文章可以看下面具体分析: http://gityuan.com/2017/06/18/SharedPreferences/ 我的项目应用场景:保存用户名字,密码,手势密码,json字符串, 如果非要保存一个你都不确定大小的东西我建议是,你单独开启一个新的sharedprefernces来保存,如果想把一个集合保存起来怎么保存,如果想把集合中在套一个map集合怎么保存, private void setCateg(List<Map<String, String>> datalist) { Gson gson = new Gson(); String s1 = gson.toJson(datalist); LSharePreference.getInstance(getActivity()).setString(SharePreferenceName.PROGRESS, s1);

} /**

这样好处,跟坏,你是怎么认为,你觉的会怎么样?

huazidev commented 4 years ago

楼上都说的很清楚了,这里补充一个问题,

SharedPreferences优化建议: 来源:http://gityuan.com/2017/06/18/SharedPreferences/#%E4%BA%94-%E6%80%BB%E7%BB%93

  1. 强烈建议不要在sp里面存储特别大的key/value, 有助于减少卡顿/anr
  2. 请不要高频地使用apply, 尽可能地批量提交;commit直接在主线程操作, 更要注意了
  3. 不要使用MODE_MULTI_PROCESS;
  4. 高频写操作的key与高频读操作的key可以适当地拆分文件, 由于减少同步锁竞争;
  5. 不要一上来就执行getSharedPreferences().edit(), 应该分成两大步骤来做, 中间可以执行其他代码.
  6. 不要连续多次edit(), 应该获取一次获取edit(),然后多次执行putxxx(), 减少内存波动; 经常看到大家喜欢封装方法, 结果就导致这种情况的出现.
  7. 每次commit时会把全部的数据更新的文件, 所以整个文件是不应该过大的, 影响整体性能;
lix-b commented 3 years ago

全量写入:无论是commit还是apply都是全量写入,所以最好一次性操作完数据再提交 安全:线程安全,进程不安全 同步异步:commit同步提交,apply异步提交 卡顿:如果文件太大,加载时间会边长,影响启动速度,处理数据不能太大,onPause的是会检查是否存储完毕

mlinqirong commented 2 years ago

SharedPreferences是线程安全的 进程不安全 用于存储体积小的数据 apply异步提交先提交到内存 而后在异步提交到硬盘 commit是同步提交到硬盘并返回结果

CKTim commented 1 year ago

SharedPreferences为什么是进程不安全? 1) SharedPreferences是进程不安全的,因为没有使用跨进程的锁。既然是进程不安全,那么久有可能在多进程操作的时候发生数据异常。

2) 我们有两个办法能保证进程安全:

使用跨进程组件,也就是ContentProvider,这也是官方推荐的做法。通过ContentProvider对多进程进行了处理,使得不同进程都是通过ContentProvider访问SharedPreferences。 加文件锁,由于SharedPreferences的本质是读写文件,所以我们对文件加锁,就能保证进程安全了。