是否应该通过UI线程访问SharedPreferences?

cot*_*aws 107 android sharedpreferences android-strictmode

随着Gingerbread的发布,我一直在尝试一些新的API,其中一个是StrictMode.

我注意到其中一个警告是为了getSharedPreferences().

这是警告:

StrictMode policy violation; ~duration=1949 ms: android.os.StrictMode$StrictModeDiskReadViolation: policy=23 violation=2
Run Code Online (Sandbox Code Playgroud)

并且它是为getSharedPreferences()在UI线程上进行的调用而给出的.

是否SharedPreferences真的应该从UI线程访问和更改?

Bra*_*ick 178

我很高兴你已经玩过了!

有些事情需要注意:( 以懒惰的子弹形式)

  • 如果这是你遇到的最糟糕的问题,你的应用程序可能就是一个好位置.:)但是,写入通常比读取慢,因此请确保使用SharedPreferenced $ Editor.apply()而不是commit().apply()是GB和async中的新功能(但始终安全,小心生命周期转换).您可以使用反射在GB +上有条件地调用apply(),在Froyo或更低版本上调用commit().我将做一个博客文章,其中包含如何执行此操作的示例代码.

关于装载,但......

  • 一旦加载,SharedPreferences就是单例并在进程范围内缓存.因此,您希望尽早加载它,以便在需要之前将其保存在内存中.(假设它很小,因为它应该是你使用SharedPreferences,一个简单的XML文件...)你不希望在将来某些用户点击按钮时出错.

  • 但无论何时调用context.getSharedPreferences(...),都会对支持XML文件进行统计,以确定它是否已更改,因此您无论如何都希望在UI事件期间避免这些统计信息.统计数据通常应该很快(并且经常被缓存),但是yaffs并没有太多的并发性(并且很多Android设备都运行在yaffs上...... Droid,Nexus One等)所以如果你避免使用磁盘,您可以避免陷入其他正在进行或待处理的磁盘操作.

  • 因此,您可能希望在onCreate()期间加载SharedPreferences并重新使用相同的实例,从而避免使用stat.

  • 但是如果你在onCreate()期间不需要你的首选项,那么加载时间会不必要地停止你的应用程序的启动,所以通常更好的是像FutureTask <SharedPreferences>这样的子类启动一个新的线程来启动.set ()FutureTask子类的值.然后只需在需要时查找FutureTask <SharedPreferences>的成员,然后查找.get()它.我打算透明地在Honeycomb的幕后制作这个.我将尝试发布一些示例代码,其中显示了该领域的最佳实践.

请查看Android开发者博客,了解下周即将发布的与StrictMode相关的主题的帖子.

  • 为了这篇精彩帖子的新读者的利益,请在下面找到@Brad Fitzpatrick上面提到的博客文章的链接:[android开发者关于Brad严格模式的博客文章](http://android-developers.blogspot.de/ 2010/12/new-gingerbread-api-strictmode.html).该帖子还有一个示例代码的链接,用于使用apply(从gingerbread开始)或commit(froyo)基于android版本存储sharedpreferences:[有条件地使用apply或commit](https://code.google.com/p /zippy-android/source/browse/trunk/examples/SharedPreferencesCompat.java) (7认同)
  • 这仍然与ICS\JB有关吗? (4认同)

mre*_*elt 6

访问共享首选项可能需要一些时间,因为它们是从闪存存储中读取的.你读了很多吗?也许你可以使用不同的格式,例如SQLite数据库.

但是不要修复使用StrictMode找到的所有内容.或者引用文档:

但是不要觉得有必要修复StrictMode找到的所有内容.特别是,在正常的活动生命周期中,通常需要许多磁盘访问的情况.使用StrictMode查找您意外执行的操作.但是,UI线程上的网络请求几乎总是一个问题.

  • 但是,SQLite也不是一个必须从闪存中读取的文件 - 但与偏好文件相比,它更大,更复杂.我一直在假设,对于与首选项相关的数据量,首选项文件将比SQLite数据库快得多. (5认同)

Tom*_*ill 5

关于 Brad 的回答的一个微妙之处:即使您在 onCreate() 中加载 SharedPreferences,您可能仍然应该在后台线程上读取值,因为 getString() 等会阻塞,直到在完成中读取共享文件首选项(在后台线程上):

public String getString(String key, String defValue) {
    synchronized (this) {
        awaitLoadedLocked();
        String v = (String)mMap.get(key);
        return v != null ? v : defValue;
    }
}
Run Code Online (Sandbox Code Playgroud)

edit() 也以同样的方式阻塞,尽管 apply() 在前台线程上似乎是安全的。

(顺便说一句,很抱歉把它放在这里。我会把它作为对布拉德回答的评论,但我刚刚加入并且没有足够的声誉来这样做。)

  • 是的,这绝对是一个考虑因素。然而,如果你调用 `get*()` ,那么你经常会处于需要结果值才能继续的阶段,在这种情况下你无论如何都必须等待,并且不会从调用 `get*()` 中受益。 get*()` 位于另一个线程上。只是一个想法。 (2认同)