是否应该在 UI 线程之外访问 SharedPreferences?
随着 Gingerbread 的发布,我一直在尝试一些新的 API,其中之一是 严格模式。
我注意到其中一个警告是针对 getSharedPreferences()
的。
这是警告:
StrictMode policy violation; ~duration=1949 ms: android.os.StrictMode$StrictModeDiskReadViolation: policy=23 violation=2
在 UI 线程上进行 getSharedPreferences()
调用时发出该警告。
SharedPreferences
访问和更改真的应该在 UI 线程之外进行吗?
With the release of Gingerbread, I have been experimenting with some of the new API's, one of them being StrictMode.
I noticed that one of the warnings is for getSharedPreferences()
.
This is the warning:
StrictMode policy violation; ~duration=1949 ms: android.os.StrictMode$StrictModeDiskReadViolation: policy=23 violation=2
and it's being given for a getSharedPreferences()
call being made on the UI thread.
Should SharedPreferences
access and changes really be made off the UI thread?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
我很高兴你已经在玩它了!
需要注意的一些事项:(以懒惰的项目符号形式)
不过,关于加载...
一旦加载,SharedPreferences 就是单例并在进程范围内缓存。因此您希望尽早加载它,以便在需要之前将其存储在内存中。 (假设它很小,如果您使用的是 SharedPreferences,一个简单的 XML 文件,那么它应该很小……)您不想在将来某个用户单击按钮时出错。
但是每当您调用 context.getSharedPreferences(...) 时,都会对支持的 XML 文件进行统计以查看它是否已更改,因此无论如何您都希望在 UI 事件期间避免这些统计信息。统计数据通常应该很快(并且通常会被缓存),但是 yaffs 没有太多并发性(并且很多 Android 设备都在 yaffs 上运行...... Droid、Nexus One 等),因此如果您避免使用磁盘,您可以避免陷入其他正在进行的或挂起的磁盘操作。
因此您可能希望在 onCreate() 期间加载 SharedPreferences 并重复使用同一实例,避免统计数据。
但是,如果您在 onCreate() 期间不需要您的首选项,则该加载时间会不必要地拖延您的应用程序的启动,因此通常最好使用像 FutureTask这样的东西。启动一个新线程以 .set() FutureTask 子类的值的子类。然后只需在需要时查找 FutureTask的成员并 .get() 它即可。我计划在 Honeycomb 中以透明的方式在幕后免费提供此服务。我将尝试发布一些示例代码
显示了该领域的最佳实践。
请查看 Android 开发者博客,了解未来几周内有关 StrictMode 相关主题的即将发布的帖子。
I'm glad you're already playing with it!
Some things to note: (in lazy bullet form)
Regarding loading, though...
once loaded, SharedPreferences are singletons and cached process-wide. so you want to get it loaded as early as possible so you have it in memory before you need it. (assuming it's small, as it should be if you're using SharedPreferences, a simple XML file...) You don't want to fault it in the future time some user clicks a button.
but whenever you call context.getSharedPreferences(...), the backing XML file is stat'd to see if it's changed, so you'll want to avoid those stats during UI events anyway. A stat should normally be fast (and often cached), but yaffs doesn't have much in the way of concurrency (and a lot of Android devices run on yaffs... Droid, Nexus One, etc.) so if you avoid disk, you avoid getting stuck behind other in-flight or pending disk operations.
so you'll probably want to load the SharedPreferences during your onCreate() and re-use the same instance, avoiding the stat.
but if you don't need your preferences anyway during onCreate(), that loading time is stalling your app's start-up unnecessarily, so it's generally better to have something like a FutureTask<SharedPreferences> subclass that kicks off a new thread to .set() the FutureTask subclasses's value. Then just lookup your FutureTask<SharedPreferences>'s member whenever you need it and .get() it. I plan to make this free behind the scenes in Honeycomb, transparently. I'll try to release some sample code which
shows best practices in this area.
Check the Android Developers blog for upcoming posts on StrictMode-related subjects in the coming week(s).
访问共享首选项可能需要相当长的时间,因为它们是从闪存读取的。你读书很多吗?也许您可以使用不同的格式,例如 SQLite 数据库。
但不要使用 StrictMode 修复您发现的所有内容。或者引用文档:
Accessing the shared preferences can take quite some time because they are read from flash storage. Do you read a lot? Maybe you could use a different format then, e.g. a SQLite database.
But don't fix everything you find using StrictMode. Or to quote the documentation:
Brad 的答案有一个微妙之处:即使您在 onCreate() 中加载 SharedPreferences,您可能仍然应该在后台线程上读取值,因为 getString() 等会阻塞,直到读取共享文件首选项完成(在后台线程上):
编辑() 也会以同样的方式阻塞,尽管 apply() 在前台线程上似乎是安全的。
(顺便说一句,很抱歉把这个写在这里。我本来想把这个作为对布拉德答案的评论,但我刚刚加入,没有足够的声誉来这样做。)
One subtlety about Brad's answer: even if you load the SharedPreferences in onCreate(), you should probably still read values on the background thread because getString() etc. block until reading the shared file preference in finishes (on a background thread):
edit() also blocks in the same way, although apply() appears to be safe on the foreground thread.
(BTW sorry to put this down here. I would have put this as a comment to Brad's answer, but I just joined and don't have enough reputation to do so.)
我知道这是一个老问题,但我想分享我的方法。我花了很长时间阅读,并使用了共享首选项和全局应用程序类的组合:
ApplicationClass:
LocalPreference:
MainActivity (在应用程序中首先调用的活动):
步骤解释:
注意:始终检查应用程序范围的变量是否不同于 NULL,原因 -> http://www.developerphil.com/dont-store-应用程序对象中的数据/
如果我没有检查 null,则在调用过滤器对象上的 getMaxDistance() 时,我将允许抛出空指针(如果应用程序对象被 Android 从内存中清除)
I know this is an old question but I want to share my approach. I had long reading times and used a combination of shared preferences and the global application class:
ApplicationClass:
LocalPreference:
MainActivity (activity that get called first in your application):
Steps explained:
NOTE: ALWAYS check if an application wide variable is different from NULL, reason -> http://www.developerphil.com/dont-store-data-in-the-application-object/
If I didn't check on null I would allow a nullpointer to be thrown when calling for example getMaxDistance() on the filter object (if the application object was swiped from the memory by Android)
SharedPreferences 类执行一些读取和操作写入磁盘上的 XML 文件,因此就像任何其他 IO 操作一样,它可能会被阻塞。当前存储在 SharedPreferences 中的数据量会影响 API 调用消耗的时间和资源。对于最少量的数据,获取/放置数据只需几毫秒(有时甚至不到一毫秒)。但从专家的角度来看,通过在后台执行 API 调用来提高性能可能很重要。对于异步 SharedPreferences,我建议查看 Datum 库。
SharedPreferences class does some reads & writes within XML files on disk, so just like any other IO operation it could be blocking. The amount of data currently stored in SharedPreferences affects the time and resource consumed by the API calls. For minimal amounts of data it's a matter of a few milliseconds (sometimes even less than a millisecond) to get/put data. But from the point of view of an expert it could be important to improve the performance by doing the API calls in background. For an asynchronous SharedPreferences I suggest checking out the Datum library.
我看不出有任何理由从后台线程读取它们。但我会写它。在启动时,共享首选项文件被加载到内存中,因此访问速度很快,但写入内容可能需要一些时间,因此我们可以使用应用写入异步。这应该是共享首选项的 commit 和 apply 方法之间的区别。
i do not see any reason to read them from a background thread. but to write it i would. at startup time the shared preference file is loaded into memory so its fast to access, but to write things can take a bit of time so we can use apply the write async. that should be the difference between commit and apply methods of shared prefs.