Android-Daily-Interview icon indicating copy to clipboard operation
Android-Daily-Interview copied to clipboard

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

Open Moosphan opened this issue 6 years ago • 12 comments

Moosphan avatar Mar 28 '19 01:03 Moosphan

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

Fritz-Xu avatar Mar 28 '19 02:03 Fritz-Xu

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

Fritz-Xu avatar Mar 28 '19 02:03 Fritz-Xu

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

leon5458 avatar Mar 28 '19 02:03 leon5458

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

mosentest avatar Apr 08 '19 04:04 mosentest

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

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

StefanShan avatar Apr 09 '19 07:04 StefanShan

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

Omooo avatar Apr 26 '19 03:04 Omooo

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

RedDargon avatar May 11 '19 05:05 RedDargon

1.SharePreferences是线程安全的 里面的方法有大量的synchronized来保障。 2.commit是同步写入,会返回执行结果,apply方法是异步写入,并不会返回执行结果; 3.SharePreferences不是进程安全的  4.SharedPreferences是以XML的格式以文件的方式自动保存的,在DDMS中的File Explorer中展开到/data/data//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);

} /**

  • 取出json */ private List<Map<String, String>> getCateg(String dateJson) { List<Map<String, String>> datalist = new ArrayList<>(); Gson gson = new Gson(); datalist = gson.fromJson(dateJson, new TypeToken<List<Map<String, String>>>() { }.getType()); return datalist; }

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

zhaoerlei1989 avatar Aug 26 '19 00:08 zhaoerlei1989

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

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时会把全部的数据更新的文件, 所以整个文件是不应该过大的, 影响整体性能;

huazidev avatar Jun 11 '20 10:06 huazidev

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

lix-b avatar Apr 05 '21 11:04 lix-b

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

mlinqirong avatar Dec 28 '21 03:12 mlinqirong

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

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

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

CKTim avatar Feb 08 '23 08:02 CKTim