Android 访问SharedReference是否应该在UI线程之外完成?
随着姜饼的发布,我一直在尝试一些新的API,其中之一就是 我注意到其中一个警告是针对Android 访问SharedReference是否应该在UI线程之外完成?,android,sharedpreferences,android-strictmode,Android,Sharedpreferences,Android Strictmode,随着姜饼的发布,我一直在尝试一些新的API,其中之一就是 我注意到其中一个警告是针对getSharedReferences() 这是警告: StrictMode policy violation; ~duration=1949 ms: android.os.StrictMode$StrictModeDiskReadViolation: policy=23 violation=2 它用于在UI线程上进行的getSharedReferences()调用 SharedReferences访问和更改是
getSharedReferences()
这是警告:
StrictMode policy violation; ~duration=1949 ms: android.os.StrictMode$StrictModeDiskReadViolation: policy=23 violation=2
它用于在UI线程上进行的getSharedReferences()
调用
SharedReferences
访问和更改是否真的应该从UI线程中进行?访问共享首选项可能需要相当长的时间,因为它们是从闪存中读取的。你读很多书吗?也许您可以使用不同的格式,例如SQLite数据库
但不要使用StrictMode修复您发现的所有问题。或引用文件:
但不要觉得必须修复StrictMode发现的所有问题。特别是,在正常的活动生命周期中,经常需要进行许多磁盘访问。使用StrictMode查找您偶然做的事情。不过,UI线程上的网络请求几乎总是一个问题
我很高兴你已经在玩它了 需要注意的事项:(以懒散的项目符号形式)
- 如果这是您最糟糕的问题,那么您的应用程序可能处于良好状态。:)不过,写入通常比读取慢,因此请确保使用的是SharedReferenced$Editor.apply()而不是commit()。apply()在GB和async中是新的(但总是安全的,注意生命周期转换)。您可以使用反射在GB+上有条件地调用apply(),在Froyo或更低版本上有条件地调用commit()。我将写一篇博客文章,其中包含如何做到这一点的示例代码
- 加载后,SharedReference是单例的,并在整个进程范围内缓存。因此,您希望尽早加载它,以便在需要它之前将其存储在内存中。(假设它很小,如果您使用的是SharedReferences,一个简单的XML文件,那么它应该很小……)您不想在将来某个用户单击按钮时出错
- 但是,无论何时调用context.getSharedReferences(…),都会对支持XML文件进行统计,以查看它是否已更改,因此无论如何,您都希望在UI事件期间避免这些统计。一个stat通常应该是快速的(并且经常被缓存),但是yaffs没有太多的并发性(而且很多Android设备都运行在yaffs上……Droid、nexusone等等),所以如果你避开磁盘,你就可以避免被其他正在运行或挂起的磁盘操作卡住
- 因此,您可能希望在onCreate()期间加载SharedReferences,并重复使用同一实例,避免使用stat
- 但是,如果您在onCreate()期间不需要您的首选项,那么加载时间会不必要地延迟应用程序的启动,因此通常最好有一个类似FutureTask的子类来启动一个新线程。set()FutureTask子类的值。然后,只要在需要时查找FutureTask的成员并.get()即可。我计划在蜂巢的幕后以透明的方式免费发布。我将尝试发布一些示例代码 展示了这方面的最佳实践
查看Android开发者博客,了解下周将要发布的关于StrictMode相关主题的帖子。Brad回答的一个微妙之处:即使你在onCreate()中加载了SharedReferences,你可能仍然应该读取后台线程上的值,因为getString()等。直到在Finishs(后台线程)中读取共享文件首选项为止: edit()也以同样的方式阻止,尽管apply()在前台线程上看起来是安全的
(顺便说一句,很抱歉把这个问题写在这里。我本想把它作为对Brad答案的评论,但我刚刚加入,没有足够的声誉这么做。)我知道这是一个老问题,但我想分享我的方法。我有很长的阅读时间,并结合使用共享首选项和全局应用程序类: 应用程序类:
public class ApplicationClass extends Application {
private LocalPreference.Filter filter;
public LocalPreference.Filter getFilter() {
return filter;
}
public void setFilter(LocalPreference.Filter filter) {
this.filter = filter;
}
}
本地首选项:
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(应用程序中首先调用的活动):
解释的步骤:
如果我没有检查null,我将允许在调用筛选器对象(如果Android从内存中刷取应用程序对象)上的例如getMaxDistance()时抛出null指针。SharedReferences类在磁盘上的XML文件中执行一些读写操作,因此就像任何其他IO操作一样,它可能会被阻塞。当前存储在SharedReferences中的数据量会影响API调用所消耗的时间和资源。对于最少量的数据,获取/放置数据只需几毫秒(有时甚至不到一毫秒)。但从专家的角度来看,通过在后台进行API调用来提高性能可能很重要。对于异步SharedReference,我建议签出库。我看不出有任何理由从后台线程读取它们。但要写它,我愿意。在启动时,共享首选项文件被加载到内存中,因此访问速度很快,但是写东西可能需要一些时间,因此我们可以使用异步写。这就是shar的commit和apply方法之间的区别
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;
}
}
}
LocalPreference.getLocalPreferences(this);