我在我的安卓应用程序中使用了SharedPreferences。 我同时使用了共享首选项的commit()和apply()方法。当我使用AVD 2.3时,没有出现错误,但是当我在AVD 2.1中运行代码时,apply()方法会出错。
那么这两个方法有什么区别呢?如果只使用commit()方法,可以在不出现任何问题的情况下存储首选项值吗?
我在我的安卓应用程序中使用了SharedPreferences。 我同时使用了共享首选项的commit()和apply()方法。当我使用AVD 2.3时,没有出现错误,但是当我在AVD 2.1中运行代码时,apply()方法会出错。
那么这两个方法有什么区别呢?如果只使用commit()方法,可以在不出现任何问题的情况下存储首选项值吗?
apply()方法是从2.3版本起被添加的,它在提交操作时不会返回表示成功或失败的布尔值。
commit()方法在保存成功时返回true,否则返回false。
Android开发团队添加了apply()方法,因为他们注意到几乎没有人关注返回值,所以apply()是异步执行的,因此更快。
http://developer.android.com/reference/android/content/SharedPreferences.Editor.html#apply()
简而言之:
commit()会同步地将数据写入(阻塞调用该方法的线程)。然后它会通知您操作的成功与否。apply()会以异步方式安排数据的写入。它不会通知您操作的成功与否。apply()保存并立即通过任何getX-方法读取,则会返回新的值!apply()且它仍在执行,则对commit()的所有调用都将被阻塞,直到所有过去的apply调用以及当前的commit调用完成为止。来自SharedPreferences.Editor文档的更详细信息:
commit()会将其首选项同步写入持久性存储,而apply()会立即将其更改提交给内存中的SharedPreferences,但开始一个异步提交到磁盘,您不会收到任何失败的通知。如果在一个apply()仍未完成时,此SharedPreferences上的另一个编辑器执行了常规的commit(),则该commit()将会阻塞,直到所有异步提交和commit()本身完成为止。
由于SharedPreferences实例在进程内是单例模式,所以如果您已经忽略了返回值,可以将任何一个commit()实例替换为apply()。
不应直接实现SharedPreferences.Editor接口。但是,如果您之前已经实现了它并且现在正在收到有关缺少apply()的错误,则可以从apply()调用commit()即可。
apply()是异步的,而且未决的写操作会阻塞对commit()的后续调用。 - spaaarky21我在使用apply()而不是commit()时遇到了一些问题。正如其他回答中所述,apply()是异步的。我的问题是,对“字符串集”偏好所做的更改从未写入持久内存。
如果你强制终止程序,或者在我安装在Android 4.1设备上的ROM中,当系统由于内存需求而杀死进程时,就会发生这种情况。
我建议如果想让您的偏好保持有效,请使用“commit()”而不是“apply()”。
commit() 同步执行,apply() 异步执行
apply() 是一个无返回值函数。
commit() 会返回true,如果新的值成功写入持久化存储。
apply() 确保在状态切换之前完成,您不需要担心Android组件的生命周期。
如果您不使用从主线程调用 commit() 的返回值,请改用 apply()
使用apply()。
它会立即将更改写入RAM,然后在稍后将其写入内部存储(实际的偏好文件)。而commit则是同步地直接将更改写入文件。
这份文档对apply()和commit()的区别有很好的解释:
commit()会同步地将其偏好设置写入持久存储,而apply()会立即将更改提交到内存中的SharedPreferences,但会启动异步提交到磁盘,并且您不会收到任何失败通知。如果在apply()尚未完成的情况下,该SharedPreferences上的另一个编辑器执行常规的commit(),则commit()将被阻塞,直到所有异步提交以及提交本身都已完成。由于SharedPreferences实例在进程中是单例的,因此,如果您已经忽略了返回值,则可以将任何commit()实例替换为apply()。
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任务的负担。 - Taseer从javadoc中得知:
与commit()不同,该方法会立即将更改提交到内存中的SharedPreferences,但会异步地将更改提交到磁盘,您无法收到任何失败通知。如果在apply()仍未完成时,此SharedPreferences上的另一个编辑器执行了常规的commit(),则commit()将阻塞,直到所有异步提交以及提交本身都已完成。
SharedPreferences preferences = App.getContext().getSharedPreferences("UserCredentials", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putString("TOKEN",token);
editor.apply();
Token在所有会话中都可用,无需担心,自动重新登录也可以在各种状态下无缝完成。
编写注销功能时,我按以下方式清除了共享首选项
SharedPreferences preferences = App.getContext().getSharedPreferences("UserCredentials", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.clear();
editor.apply()
简而言之,您可以简单地写成:
preferences.edit().clear().apply()
为了确保我清除了缓存,我会在注销之前进行日志记录,preferences.getString("TOKEN"); 显示为 null。
在重新启动主活动(因为它以登录片段开始)之后 - 我会再次检查令牌,使用:
SharedPreferences preferences = App.getContext().getSharedPreferences("UserCredentials", MODE_PRIVATE);
String retrievedToken = preferences.getString("TOKEN",null); // Second param = default value
Log.d(TAG, "RETRIEVING TOKEN: " + retrievedToken);
即使在注销之前清除了令牌,该令牌实际上还是出现了。
(导致注销 ->“使用令牌登录循环)
只有在设置和清除令牌时都添加editor.commit();,令牌才会真正消失。
请注意,我使用的是模拟器。
这种行为实际上是有道理的,因为内存中的存储已更新,但异步调用在应用程序重新启动之前未完成。
因此,commit();将强制应用程序等待(同步地)执行实际的后续命令,如system.exit()等,而apply()如果其他命令强制应用程序进行某种状态更改,则可能失败。
为了确保您击中所有正确的位置,您始终可以连续使用apply()和commit()。
apply()函数会异步执行磁盘 I/O 操作,而commit()函数则是同步的。因此,在UI线程中不应该调用commit()函数。 - okonomichiyakiapply()的对象会胜出。因此,如果您确保应用程序只使用一个SharedPreferences.Editor,则可以安全地使用apply()代替commit()。 - aoeucommit()时,有没有一种方法可以禁用Lint警告? - QED