我在我的android应用程序中使用SharedPreferences。我同时使用commit()和apply()方法共享首选项。当我使用avd2.3时,它显示没有错误,但当我在avd2.1中运行代码时,apply()方法显示错误。

这两者有什么区别呢?通过只使用commit()我可以存储首选项值没有任何问题吗?


当前回答

文档很好地解释了apply()和commit()之间的区别:

Unlike commit(), which writes its preferences out to persistent storage synchronously, apply() commits its changes to the in-memory SharedPreferences immediately but starts an asynchronous commit to disk and you won't be notified of any failures. If another editor on this SharedPreferences does a regular commit() while a apply() is still outstanding, the commit() will block until all async commits are completed as well as the commit itself. As SharedPreferences instances are singletons within a process, it's safe to replace any instance of commit() with apply() if you were already ignoring the return value.

其他回答

文档很好地解释了apply()和commit()之间的区别:

Unlike commit(), which writes its preferences out to persistent storage synchronously, apply() commits its changes to the in-memory SharedPreferences immediately but starts an asynchronous commit to disk and you won't be notified of any failures. If another editor on this SharedPreferences does a regular commit() while a apply() is still outstanding, the commit() will block until all async commits are completed as well as the commit itself. As SharedPreferences instances are singletons within a process, it's safe to replace any instance of commit() with apply() if you were already ignoring the return value.

tl; diana:

commit() writes the data synchronously (blocking the thread its called from). It then informs you about the success of the operation. apply() schedules the data to be written asynchronously. It does not inform you about the success of the operation. If you save with apply() and immediately read via any getX-method, the new value will be returned! If you called apply() at some point and it's still executing, any calls to commit() will block until all past apply-calls and the current commit-call are finished.

更多深入的信息来自SharedPreferences。编辑文档:

Unlike commit(), which writes its preferences out to persistent storage synchronously, apply() commits its changes to the in-memory SharedPreferences immediately but starts an asynchronous commit to disk and you won't be notified of any failures. If another editor on this SharedPreferences does a regular commit() while a apply() is still outstanding, the commit() will block until all async commits are completed as well as the commit itself. As SharedPreferences instances are singletons within a process, it's safe to replace any instance of commit() with apply() if you were already ignoring the return value. The SharedPreferences.Editor interface isn't expected to be implemented directly. However, if you previously did implement it and are now getting errors about missing apply(), you can simply call commit() from apply().

我使用apply()而不是commit()遇到了一些问题。正如前面在其他响应中所述,apply()是异步的。我得到的问题是,形成一个“字符串集”偏好的变化从来没有写到持久内存。

如果你“强制扣留”程序,或者在我安装在Android 4.1设备上的ROM中,由于内存需求进程被系统杀死,就会发生这种情况。

如果你想激活你的首选项,我建议使用“commit()”而不是“apply()”。

在使用web API时,我注意到apply()在注销前使用共享首选项时的主要缺点。

请看下面的场景: 用户登录和令牌(用于自动重新登录)随POST一起传递。

SharedPreferences preferences = App.getContext().getSharedPreferences("UserCredentials", Context.MODE_PRIVATE);

SharedPreferences.Editor editor = preferences.edit();

editor.putString("TOKEN",token);

editor.apply();

令牌在所有会话中都可用,没有问题,自动重新记录在整个状态中没有任何进一步的问题。

在编写注销函数时,我清除了共享首选项,如下所示

SharedPreferences preferences = App.getContext().getSharedPreferences("UserCredentials", Context.MODE_PRIVATE);

SharedPreferences.Editor editor = preferences.edit();

editor.clear();

editor.apply()

简而言之,你可以这样写:

preferences.edit().clear().apply()

为了确保我清除了缓存,我会在注销前Log,和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()。

Commit()是同步的,apply()是异步的 Apply()是void函数。 如果新值成功写入持久存储,Commit()返回true。 apply()保证在切换状态之前完成,你不需要担心Android组件的生命周期

如果你不使用commit()返回的值,而你在主线程中使用commit(),请使用apply()而不是commit()