Должен ли доступ к SharedPreferences выполняться из потока пользовательского интерфейса?
С выпуском Gingerbread я экспериментировал с некоторыми из новых API, одним из которых является StrictMode.
Я заметил, что одно из предупреждений для getSharedPreferences()
.
это предупреждение:
StrictMode policy violation; ~duration=1949 ms: android.os.StrictMode$StrictModeDiskReadViolation: policy=23 violation=2
и это дается за getSharedPreferences()
вызов выполняется в потоке пользовательского интерфейса.
должны SharedPreferences
доступ и изменения действительно будут сделаны из потока пользовательского интерфейса?
4 ответов
Я рад, что вы уже играете с ним!
обратите внимание: (в ленивой форме пули)
- если это худшая из ваших проблем, ваше приложение, вероятно, в хорошем месте. :) Записи обычно медленнее, чем чтение, поэтому убедитесь, что вы используете SharedPreferenced$Editor.применить () вместо commit (). apply () является новым в GB и async (но всегда безопасным, осторожным с переходами жизненного цикла). Вы можете использовать отражение для условного вызова apply() на GB+ и commit () на Froyo или ниже. Я буду делать blogpost с образцом кода того, как это сделать.
Что касается загрузки, хотя...
после загрузки SharedPreferences являются синглетами и кэшируются в масштабах всего процесса. поэтому вы хотите загрузить его как можно раньше, чтобы он был в памяти, прежде чем вам понадобится. (предполагая, что он маленький, как и должно быть, если вы используете SharedPreferences, простой XML-файл...) Вы не хотите винить его в будущем времени какой-то пользователь нажимает кнопку.
но всякий раз, когда вы называете контекстом.getSharedPreferences(...), резервный XML-файл stat'D, чтобы увидеть, если он изменился, так что вы хотите, чтобы избежать этой статистики во время событий пользовательского интерфейса в любом случае. Обычно стат должен быть быстрым (и часто кэшироваться), но yaffs не имеет большого параллелизма (и многие устройства Android работают на yaffs... Дроид, Нексус один и т. д.) поэтому, если вы избегаете диска, вы избегаете застрять за другим в полете или в ожидании диска оперативный.
поэтому вы, вероятно, захотите загрузить SharedPreferences во время onCreate() и повторно использовать тот же экземпляр, избегая статистики.
но если вам все равно не нужны ваши предпочтения во время onCreate(), это время загрузки задерживает запуск вашего приложения без необходимости, поэтому обычно лучше иметь что-то вроде подкласса FutureTask
, который запускает новый поток .set () значение подклассов FutureTask. Затем просто найдите член FutureTask всякий раз, когда вам это нужно .получить его. Я планирую сделать это бесплатно за кулисами в Honeycomb, прозрачно. Я попробую выпустить некоторый код образца который показывает лучшие практики в этой области.
Проверьте блог разработчиков Android для предстоящих сообщений по темам, связанным со StrictMode в ближайшие недели (ы).
доступ к общим предпочтениям может занять довольно много времени, потому что они считываются из флэш-памяти. Ты много читаешь? Возможно, вы могли бы использовать другой формат, например, базу данных SQLite.
но не исправить все, что вы найдете с помощью StrictMode. Или процитировать документацию:
но не чувствуйте себя обязанным исправлять все, что находит StrictMode. В частности, многие случаи доступа к дискам часто необходимы в течение обычного жизненного цикла активности. Использовать StrictMode, чтобы найти вещи, которые вы сделали случайно. Однако сетевые запросы в потоке пользовательского интерфейса почти всегда являются проблемой.
одна тонкость ответа Брэда: даже если вы загружаете SharedPreferences в onCreate(), вы, вероятно, все равно должны читать значения в фоновом потоке, потому что getString() и т. д. блокируйте до чтения предпочтения общего файла в finishes (в фоновом потоке):
public String getString(String key, String defValue) {
synchronized (this) {
awaitLoadedLocked();
String v = (String)mMap.get(key);
return v != null ? v : defValue;
}
}
edit() также блокирует таким же образом, хотя apply () кажется безопасным в потоке переднего плана.
(кстати, извините, что положил это здесь. Я бы поставил это как комментарий к Брэду ответьте, но я только что присоединился, и у меня недостаточно репутации для этого.)
Я знаю, это старый вопрос, но я хочу поделиться своим подходом. Я долго читал и использовал комбинацию общих настроек и глобального класса приложений:
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);
действия:
- основное действие вызывает getLocalPreferences (this) - > это прочитает ваш настройки, установите объект фильтра в классе приложения и возвращает его.
- когда вы снова вызываете функцию getLocalPreferences () где-то еще в приложении, она сначала проверяет, доступна ли она в классе приложения, который намного быстрее.
Примечание: всегда проверяйте, отличается ли широкая переменная приложения от NULL, причина ->http://www.developerphil.com/dont-store-data-in-the-application-object/
объект приложения не останется в памяти навсегда, он будет убит. Вопреки распространенному мнению, приложение не будет перезапущен с нуля. Android создаст новый объект приложения и начнет работу там, где пользователь был раньше, чтобы создать иллюзию, что приложение никогда не было убито в первую очередь.
Если бы я не проверял null, я бы позволил nullpointer быть брошенным при вызове, например getMaxDistance() на объекте фильтра (если объект приложения был удален из памяти Android)