应该访问SharedPreferencesclosuresUI线程?

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

我注意到其中的一个警告是getSharedPreferences()

这是警告:

 StrictMode policy violation; ~duration=1949 ms: android.os.StrictMode$StrictModeDiskReadViolation: policy=23 violation=2 

并且正在为在UI线程上进行的getSharedPreferences()调用给出。

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

我很高兴你已经在玩它了!

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

  • 如果这是你的问题最糟糕的,你的应用程序可能是一个很好的位置。 :)写入通常比读取要慢,所以请确保使用SharedPreferenced $ Editor.apply()而不是commit()。 apply()在GB和async中是新的(但总是安全的,谨慎的生命周期转换)。 您可以使用reflection来有条件地调用GB +上的apply()和Froyo或更低版本上的commit()。 我会做一个如何做到这一点的示例代码的博客。

关于加载,虽然…

  • 一旦加载,SharedPreferences是单例,并在整个过程中caching。 所以你想尽可能早地加载它,所以你需要它之​​前在内存中。 (假设它很小,因为它应该是如果你使用SharedPreferences,一个简单的XML文件…)你不想在将来某个用户点击一个button时错误的。

  • 但是无论何时调用context.getSharedPreferences(…),都会对后备XML文件进行统计,以查看它是否已更改,因此无论如何您都需要在UI事件期间避免使用这些统计信息。 一个统计通常应该是快速的(通常是caching),但是yaffs并没有很多并发的方式(和大量的Android设备在yaffs上运行… Droid,Nexus One等),所以如果你避免磁盘,您可以避免陷入其他正在进行的或正在进行的磁盘操作之中。

  • 所以你可能想要在你的onCreate()期间加载SharedPreferences,并重新使用同一个实例,避免stat。

  • 但是如果你在onCreate()过程中不需要你的偏好,这个加载时间就会不必要地拖延你的应用程序的启动,所以通常最好有一个像FutureTask <SharedPreferences>子类一样的新子类来启动.set ()FutureTask子类的值。 然后只要你需要它和.get()它就查找你的FutureTask <SharedPreferences>的成员。 我计划透过Honeycomb幕后制作。 我将尝试发布一些示例代码,以显示这方面的最佳实践。

查看Android Developers博客,了解即将到来的与StrictMode相关主题的即将发布的post。

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

但不要修复使用StrictMode发现的所有内容。 或者引用文档:

但是不要强迫所有StrictMode发现。 特别是,在正常的活动生命周期中,通常需要很多磁盘访问的情况。 使用StrictMode来查找你意外做的事情。 但是,UI线程上的networking请求几乎总是一个问题。

一个关于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; } } 

编辑()也以相同的方式阻止,尽pipeapply()在前台线程中看起来是安全的。

(不好意思把这个放在这里,我会把这个作为对Brad答案的一个评论,但是我刚join,没有足够的声望去这样做。)

我知道这是一个老问题,但我想分享我的方法。 我有很长的阅读时间,并使用了共享偏好和全局应用程序类的组合:

ApplicationClass:

 public class ApplicationClass extends Application { private LocalPreference.Filter filter; public LocalPreference.Filter getFilter() { return filter; } public void setFilter(LocalPreference.Filter filter) { this.filter = filter; } } 

LocalPreference:

 public class LocalPreference { public static void saveLocalPreferences(Activity activity, int maxDistance, int minAge, int maxAge, boolean showMale, boolean showFemale) { Filter filter = new Filter(); filter.setMaxDistance(maxDistance); filter.setMinAge(minAge); filter.setMaxAge(maxAge); filter.setShowMale(showMale); filter.setShowFemale(showFemale); BabysitApplication babysitApplication = (BabysitApplication) activity.getApplication(); babysitApplication.setFilter(filter); SecurePreferences securePreferences = new SecurePreferences(activity.getApplicationContext()); securePreferences.edit().putInt(Preference.FILER_MAX_DISTANCE.toString(), maxDistance).apply(); securePreferences.edit().putInt(Preference.FILER_MIN_AGE.toString(), minAge).apply(); securePreferences.edit().putInt(Preference.FILER_MAX_AGE.toString(), maxAge).apply(); securePreferences.edit().putBoolean(Preference.FILER_SHOW_MALE.toString(), showMale).apply(); securePreferences.edit().putBoolean(Preference.FILER_SHOW_FEMALE.toString(), showFemale).apply(); } public static Filter getLocalPreferences(Activity activity) { BabysitApplication babysitApplication = (BabysitApplication) activity.getApplication(); Filter applicationFilter = babysitApplication.getFilter(); if (applicationFilter != null) { return applicationFilter; } else { Filter filter = new Filter(); SecurePreferences securePreferences = new SecurePreferences(activity.getApplicationContext()); filter.setMaxDistance(securePreferences.getInt(Preference.FILER_MAX_DISTANCE.toString(), 20)); filter.setMinAge(securePreferences.getInt(Preference.FILER_MIN_AGE.toString(), 15)); filter.setMaxAge(securePreferences.getInt(Preference.FILER_MAX_AGE.toString(), 50)); filter.setShowMale(securePreferences.getBoolean(Preference.FILER_SHOW_MALE.toString(), true)); filter.setShowFemale(securePreferences.getBoolean(Preference.FILER_SHOW_FEMALE.toString(), true)); babysitApplication.setFilter(filter); return filter; } } public static class Filter { private int maxDistance; private int minAge; private int maxAge; private boolean showMale; private boolean showFemale; public int getMaxDistance() { return maxDistance; } public void setMaxDistance(int maxDistance) { this.maxDistance = maxDistance; } public int getMinAge() { return minAge; } public void setMinAge(int minAge) { this.minAge = minAge; } public int getMaxAge() { return maxAge; } public void setMaxAge(int maxAge) { this.maxAge = maxAge; } public boolean isShowMale() { return showMale; } public void setShowMale(boolean showMale) { this.showMale = showMale; } public boolean isShowFemale() { return showFemale; } public void setShowFemale(boolean showFemale) { this.showFemale = showFemale; } } } 

MainActivity(在您的应用程序中首先被调用的活动):

 LocalPreference.getLocalPreferences(this); 

步骤解释:

  1. 主要的活动调用getLocalPreferences(this) – >这将读取您的首选项,设置应用程序类中的filter对象,并返回它。
  2. 当您在应用程序的其他地方再次调用getLocalPreferences()函数时,它首先会检查在应用程序类中它是否不可用,速度要快得多。

注:总是检查一个应用程序的广泛variables是否与NULL不同,原因 – > http://www.developerphil.com/dont-store-data-in-the-application-object/

应用程序对象不会永远留在内存中,它会被杀死。 与stream行的观点相反,应用程序不会从头开始重新启动。 Android将创build一个新的Application对象,并启动用户以前的活动,以使幻想中的应用程序从未被杀死。

如果我没有检查null,我将允许在filter对象上调用示例getMaxDistance()时抛出一个空指针(如果应用程序对象是由Android从内存中移出的)