随着Gingerbread的发布,我一直在尝试一些新的API,其中一个是StrictMode。
我注意到其中一个警告是getSharedPreferences()
。
这是警告:
StrictMode policy violation; ~duration=1949 ms: android.os.StrictMode$StrictModeDiskReadViolation: policy=23 violation=2
正在为UI线程进行getSharedPreferences()
调用。
是否真的要从UI线程SharedPreferences
访问和更改?
答案 0 :(得分:174)
我很高兴你已经玩过它了!
有些注意事项:(以懒惰的子弹形式)
关于加载,但是......
加载后,SharedPreferences是单例并在进程范围内缓存。因此,您希望尽早加载它,以便在需要之前将其保存在内存中。 (假设它很小,因为它应该是你使用SharedPreferences,一个简单的XML文件......)你不希望在将来有些用户点击按钮的时候将它弄错。
但是无论何时调用context.getSharedPreferences(...),都会对支持XML文件进行统计,以确定它是否已更改,因此您无论如何都希望在UI事件期间避免这些统计信息。统计数据通常应该很快(并且经常被缓存),但是yaffs并没有太多的并发性(并且很多Android设备都运行在yaffs上...... Droid,Nexus One等)所以如果你避免使用磁盘,您可以避免陷入其他正在进行或待处理的磁盘操作中。
因此您可能希望在onCreate()期间加载SharedPreferences并重新使用相同的实例,从而避免使用stat。
但是如果你在onCreate()期间无论如何都不需要你的偏好,那么加载时间会不必要地拖延你的app的启动,所以通常更好的方法是使用FutureTask< SharedPreferences>这样的东西。将新线程启动到.set()FutureTask子类的值的子类。然后只需在需要时查找FutureTask< SharedPreferences>的成员,然后查找.get()它。我打算透明地在Honeycomb的幕后制作这个。我将尝试发布一些示例代码 展示了该领域的最佳实践。
查看Android开发者博客,了解即将在下周发布的与StrictMode相关主题的帖子。
答案 1 :(得分:5)
访问共享首选项可能需要一些时间,因为它们是从闪存存储中读取的。你读了很多吗?也许你可以使用不同的格式,例如一个SQLite数据库。
但是不要修复使用StrictMode找到的所有内容。或者引用文档:
但是不要觉得有必要修复StrictMode找到的所有东西。特别是,在正常的活动生命周期中,通常需要许多磁盘访问的情况。使用StrictMode查找您意外执行的操作。但是,UI线程上的网络请求几乎总是一个问题。
答案 2 :(得分:4)
关于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;
}
}
edit()也以相同的方式阻塞,虽然apply()似乎在前台线程上是安全的。
(顺便说一句,很抱歉把它放在这里。我会把这作为对布拉德答案的评论,但我刚刚加入并没有足够的声誉这样做。)
答案 3 :(得分:1)
我知道这是一个老问题,但我想分享我的方法。我有很长的阅读时间,并使用了共享首选项和全局应用程序类的组合:
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);
步骤解释:
注意:始终检查应用程序范围变量是否与NULL不同,原因 - > http://www.developerphil.com/dont-store-data-in-the-application-object/
应用程序对象永远不会留在内存中,它会被杀死。与流行的看法相反,该应用程序将不会从头开始重新启动。 Android将创建一个新的Application对象并启动用户之前的活动,以便首先假定该应用程序从未被杀死。
如果我没有检查null,我会在过滤器对象上调用例如getMaxDistance()时允许抛出nullpointer(如果应用程序对象是由Android从内存中刷过的话)
答案 4 :(得分:0)
SharedPreferences类在磁盘上的XML文件中执行一些读写操作,因此就像其他任何IO操作一样,它可能会阻塞。当前存储在SharedPreferences中的数据量会影响API调用消耗的时间和资源。对于最小量的数据,获取/放入数据只需几毫秒(有时甚至不到一毫秒)。但是从专家的角度来看,通过在后台进行API调用来提高性能可能很重要。对于异步SharedPreferences,我建议签出Datum库。
答案 5 :(得分:0)
我看不到从后台线程读取它们的任何理由。但是我会写。在启动时,共享的首选项文件已加载到内存中,因此可以快速访问它,但是写东西可能会花费一些时间,因此我们可以使用apply write async。这应该是共享首选项的commit和apply方法之间的区别。