Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
Moosphan opened this issue Mar 28, 2019 · 12 comments

Comments

@Moosphan
Copy link
Owner

No description provided.

@Moosphan Moosphan added the underway the daily question is solving now label Mar 28, 2019
@Fritz-Xu
Copy link

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

@Fritz-Xu
Copy link

Fritz-Xu commented Mar 28, 2019

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

@leon5458
Copy link

leon5458 commented Mar 28, 2019

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

@mosentest
Copy link

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

@StefanShan
Copy link

StefanShan commented Apr 9, 2019

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

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

@Moosphan Moosphan added Android存储方式 and removed underway the daily question is solving now labels Apr 11, 2019
@Omooo
Copy link

Omooo commented Apr 26, 2019

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

@RedDargon
Copy link

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
Copy link

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);

}
/**

  • 取出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;
    }

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

@huazidev
Copy link

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

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
Copy link

lix-b commented Apr 5, 2021

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

@mlinqirong
Copy link

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

@CKTim
Copy link

CKTim commented Feb 8, 2023

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

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

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests