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() 有啥区别的主要内容,如果未能解决你的问题,请参考以下文章

片段中的Android SharedPreferences

删除 sharedPreferences 列表中的一个元素

将 SharedPreferences 值传递给 Flutter 中的另一个方法

SharedPreferences-静态类中的静态字符串?

如何将 SharedPreferences 中的数据保存并显示为列表?

Flutter——SharedPreferences