SharedPreferences 中的 commit() 和 apply() 有啥区别
Posted
技术标签:
【中文标题】SharedPreferences 中的 commit() 和 apply() 有啥区别【英文标题】:What's the difference between commit() and apply() in SharedPreferencesSharedPreferences 中的 commit() 和 apply() 有什么区别 【发布时间】:2011-08-23 01:44:26 【问题描述】:我在我的安卓应用中使用SharedPreferences
。我正在使用共享偏好中的commit()
和apply()
方法。当我使用 AVD 2.3 时,它没有显示错误,但是当我在 AVD 2.1 中运行代码时,apply()
方法显示错误。
那么这两者有什么区别呢?并且只使用commit()
可以毫无问题地存储偏好值吗?
【问题讨论】:
这是一岁了,但我还是要评论它,虽然它可能很明显,但没有一个答案表明这一点:apply()
将异步执行磁盘 I/O 而commit()
是同步的。所以你真的不应该从 UI 线程调用commit()
。
注意,当多个 SharedPreferences.Editor 对象在使用时,最后一个调用apply()
的对象获胜。因此,如果您确保您的应用程序只使用一个 SharedPreferences.Editor,您可以安全地使用 apply()
代替 commit()
。
根据 android Studio Lint 警告:commit() 将立即同步保存数据。但是, apply() 将异步保存它(在后台),从而提高一些性能。这就是为什么如果你不关心它的返回类型(如果数据保存成功与否), apply() 优于 commit()。
有没有办法在使用commit()
时禁用Lint警告?
【参考方案1】:
apply()
是在 2.3 中添加的,它提交而不返回指示成功或失败的布尔值。
commit()
如果保存成功则返回 true,否则返回 false。
apply()
被添加是因为 Android 开发团队注意到几乎没有人注意到返回值,因此 apply 更快,因为它是异步的。
http://developer.android.com/reference/android/content/SharedPreferences.Editor.html#apply()
【讨论】:
这个答案是正确的,但我猜@spacemanaki 上面的评论也是正确的,包含有价值的信息 commit() 将其数据立即写入持久存储,而 apply() 将在后台处理它。 它会产生竞争条件吗? 如果我用 apply() 写了一些东西并尝试在之后立即阅读会发生什么?阅读是否保证能给我最新的价值?文档说,如果在您触发 apply() 后发生另一个 commit(),则 commit() 将阻塞,直到 apply() 持久化到磁盘,这清楚地表明在“写入”操作时不会发生此问题,但是如果您在之后立即写作和阅读呢?从我的测试中,返回了最新的值,但我想知道这是否 100% 保证。 用 apply() 替换任何 commit() 实例是安全的,请参阅 developer.android.com/reference/android/content/…【参考方案2】:tl;博士:
commit()
同步写入数据(阻塞调用它的线程)。然后它通知您操作成功。
apply()
安排异步写入数据。它不会通知您操作是否成功。
如果您使用apply()
保存并通过任何getX 方法立即读取,则将返回新 值!
如果您在某个时间点调用了 apply()
并且它仍在执行,则对 commit()
的任何调用都将阻塞,直到所有过去的应用调用和当前的提交调用都完成。
来自SharedPreferences.Editor 文档的更深入信息:
与 commit() 不同,后者将其写入 偏好到持久存储 同步,apply() 提交它的 内存中的变化 SharedPreferences 立即但 开始异步提交到磁盘 并且您不会收到任何通知 失败。如果其他编辑对此 SharedPreferences 做一个常规的 commit() 而 apply() 仍然 未完成,commit() 将阻塞 直到所有异步提交完成 以及提交本身。
由于 SharedPreferences 实例是 进程中的单例,它是安全的 替换任何 commit() 实例 与 apply() 如果你已经 忽略返回值。
SharedPreferences.Editor 接口 预计不会实施 直接地。但是,如果您之前 确实实现了它,现在得到 关于缺少 apply() 的错误,您可以 只需从 apply() 调用 commit()。
【讨论】:
这是一个更好的答案,因为它提到apply()
是异步的,并且挂起的写入会阻止未来对 commit()
的调用。【参考方案3】:
我在使用 apply() 而不是 commit() 时遇到了一些问题。如前在其他响应中所述,apply() 是异步的。我遇到的问题是,对“字符串集”首选项的更改永远不会写入持久内存。
如果您“强制保留”程序,或者在我的 Android 4.1 设备上安装的 ROM 中,当进程因内存需求而被系统终止时,就会发生这种情况。
如果您希望自己的偏好生效,我建议使用“commit()”而不是“apply()”。
【讨论】:
您确定您的问题与并发线程无关吗?发送 apply() 后,必须等待一段时间才能读取添加的内容,否则 UI 线程会在 apply() 的工作线程提交更改之前尝试读取。 关于字符串集,***.com/questions/16820252/… @JoseLSegura - 文档另有建议:developer.android.com/intl/ja/reference/android/content/…“您无需担心 Android 组件生命周期及其与 apply() 写入磁盘的交互。该框架确保飞行中的磁盘来自 apply() 的写入在切换状态之前完成。”我想知道您所看到的是否是 Android 中的错误,如果是,是否已在较新版本中修复。 使用库“ProcessPhoenix”重置我的应用程序时,我遇到了同样的问题。我在执行重置之前保存了首选项,但“应用”不起作用。【参考方案4】:commit()
是同步的,apply()
是异步的
apply()
是无效函数。
commit()
如果新值已成功写入持久存储,则返回 true。
apply()
保证在切换状态之前完成,您无需担心Android组件生命周期
如果您不使用从commit()
返回的值并且您正在使用主线程中的commit()
,请使用apply()
而不是commit()
【讨论】:
为了清楚起见,那些“切换状态”是什么? @carloswm85 2 岁,但要澄清一下:通常“切换状态”意味着诸如方向改变之类的事件。【参考方案5】:使用 apply()。
它立即将更改写入 RAM,然后等待并将其写入内部存储(实际的首选项文件)。提交将更改同步并直接写入文件。
【讨论】:
【参考方案6】:文档很好地解释了apply()
和commit()
之间的区别:
与
commit()
不同,后者将其偏好写入持久性 同步存储,apply()
将其更改提交到内存中SharedPreferences
立即开始异步提交 磁盘,您将不会收到任何故障通知。如果另一个编辑 这个SharedPreferences
执行常规commit()
而apply()
是 仍然未完成,commit()
将阻塞,直到所有异步提交都完成 完成以及提交本身。作为SharedPreferences
实例 是进程中的单例,替换任何实例都是安全的commit()
和apply()
如果你已经忽略了返回值。
【讨论】:
【参考方案7】:来自 javadoc:
与 commit() 不同,commit() 将其写入 偏好到持久存储 同步地,apply() 提交它的 内存中的变化 SharedPreferences 立即但 开始异步提交到磁盘 并且您不会收到任何通知 失败。如果此 SharedPreferences 上的另一个编辑器在 > apply() 仍然未完成时执行常规 commit(),则 commit() 将阻塞,直到所有异步提交以及提交本身都完成
【讨论】:
【参考方案8】:commit() 和 apply() 的区别
当我们使用 SharedPreference 时,我们可能会对这两个术语感到困惑。基本上它们可能是相同的,所以让我们澄清一下commit()和apply()的区别。
1.返回值:
apply()
提交时不返回指示成功或失败的布尔值。
commit(
) 如果保存成功则返回 true,否则返回 false。
速度:
apply()
更快。
commit()
比较慢。
异步与异步同步:
apply()
: 异步
commit()
: 同步
原子:
apply()
: 原子的
commit()
: 原子的
错误通知:
apply()
: 没有
commit()
:是的
【讨论】:
apply()
比commit()
“快”多少?它们本质上代表将被放入线程的 Looper 中的相同任务。 commit()
将该任务放在主 Looper 中,而 apply()
将其放在后台,从而使主 Looper 没有磁盘 I/O 任务。
与 commit() 不同,它将其首选项同步写入持久存储,apply() 立即将其更改提交到内存中的 SharedPreferences 但开始异步提交到磁盘并且不会通知您任何失败。如果此 SharedPreferences 上的另一个编辑器在 apply() 仍然未完成时执行常规 commit(),则 commit() 将阻塞,直到所有异步提交完成以及提交本身参见 DOC developer.android.com/reference/android/content/…【参考方案9】:
apply() 立即更改内存中的 SharedPreferences 对象,但将更新异步写入磁盘。
commit() 将数据同步写入磁盘。但是因为 commit() 是同步的,你应该避免从你的主线程调用它,因为它可能会暂停你的 UI 渲染。
【讨论】:
不应该是“直到apply()完成”吗?以上是关于SharedPreferences 中的 commit() 和 apply() 有啥区别的主要内容,如果未能解决你的问题,请参考以下文章
将 SharedPreferences 值传递给 Flutter 中的另一个方法