2010-12-06 3 views
90

С выпуском Gingerbread, я экспериментировал с некоторыми из новых API, один из которых был StrictMode.Должен ли доступ к SharedPreferences делать из потока пользовательского интерфейса?

Я заметил, что одно из предупреждений предназначено для getSharedPreferences().

Это предупреждение:

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

и это время дается для getSharedPreferences() вызова делаются в потоке пользовательского интерфейса.

Должно ли SharedPreferences доступ и изменения действительно сделаны из нити пользовательского интерфейса?

+0

I Я всегда делал свои операции с предпочтением в потоке пользовательского интерфейса. Хотя я предполагаю, что это имеет смысл, так как это операция ввода-вывода – Falmarri 2010-12-06 21:53:40

ответ

157

Я рад, что вы уже играете с ним!

Некоторые вещи, чтобы отметить: (в ленивой форме пули)

  • если это худший из ваших проблем, ваше приложение, вероятно, в хорошем месте. :) Записи, как правило, медленнее, чем чтение, поэтому убедитесь, что вы используете SharedPreferenced $ Editor.apply() вместо commit(). apply() является новым в GB и асинхронным (но всегда безопасным, тщательным переходом на жизненный цикл). Вы можете использовать отражение для условного вызова apply() на GB + и commit() на Froyo или ниже. Я буду делать blogpost с образцом кода, как это сделать.

Что касается нагрузки, хотя ...

  • после загрузки SharedPreferences одноэлементны и кэшируются процесс в масштабах. поэтому вы хотите загрузить его как можно раньше, чтобы иметь его в памяти, прежде чем он вам понадобится. (предполагая, что он мал, как и должно быть, если вы используете SharedPreferences, простой XML-файл ...). Вы не хотите, чтобы это произошло в будущем, некоторые пользователи нажимают кнопку.

  • , но всякий раз, когда вы вызываете context.getSharedPreferences (...), файл базы данных поддержки является stat'd, чтобы увидеть, изменилось ли оно, поэтому вы хотите избежать такой статистики во время событий пользовательского интерфейса. Стат обычно должен быть быстрым (и часто кэшированным), но у yaffs не так много на пути параллелизма (и многие Android-устройства работают на yaffs ... Droid, Nexus One и т. Д.), Поэтому, если вы избежите диска , вы избегаете застревания за другими в полете или в ожидании операций на диске.

  • , так что вы, вероятно, захотите загрузить SharedPreferences во время своего onCreate() и повторно использовать один и тот же экземпляр, избегая stat.

  • , но если вам не нужны ваши предпочтения в любом случае в течение OnCreate(), что время загрузки буксует их запуск вашего приложения без необходимости, так что в целом лучше иметь что-то вроде FutureTask <SharedPreferences> подкласса, который пинает new thread to .set() значение подкласса FutureTask. Затем просто найдите свой FutureTask <SharedPreferences>, если вам это нужно, и .get(). Я планирую сделать это бесплатно за кадром в Honeycomb, прозрачно. Я попытаюсь выпустить пример кода, который показывает лучшие практики в этой области.

Отметьте блог разработчиков Android для предстоящих сообщений по темам, связанным с StrictMode, на следующей неделе.

5

Доступ к общим настройкам может занять довольно много времени, поскольку они считываются из флэш-памяти. Вы много читаете? Возможно, вы могли бы использовать другой формат, например, база данных SQLite.

Но не исправляйте все, что вы найдете, используя StrictMode. Или процитировать документацию:

Но не чувствуйте себя обязанным исправить все, что обнаружил StrictMode. В частности, во время обычного жизненного цикла активности часто требуется много случаев доступа к диску. Используйте StrictMode, чтобы найти то, что вы делали случайно. Однако сетевые запросы в потоке пользовательского интерфейса почти всегда являются проблемой.

+5

Но не является SQLite также файлом, который должен быть прочитан из флэш-памяти, - но более сложным и более сложным по сравнению с файлом настроек. Я предполагаю, что для количества данных, связанных с настройками, файл предпочтений будет намного быстрее, чем база данных SQLite. – Tom 2012-10-12 16:41:56

+0

Это правильно. Как уже упоминал Брэд, это почти всегда не проблема - и он также упоминает, что некорректно загружать SharedPreferences один раз (возможно, даже в потоке с использованием FutureTask) и удерживать его для любого возможного доступа к одному экземпляру. – mreichelt 2012-10-22 16:42:46

3

Одна уловка о ответе Брэда: даже если вы загружаете SharedPreferences в onCreate(), вы, вероятно, должны читать значения в фоновом потоке, потому что getString() и т. Д. Блокируют до чтения предпочтений общего файла в финишах (на фоне нить):

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

редактировать() также блоки таким же образом, хотя и применяются() является безопасным на переднем плане резьбы.

(кстати жаль поставить это здесь. Я бы поставил это как комментарий к ответу Брэда, но я только что присоединился и не имеют достаточной репутацией, чтобы сделать это.)

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); 

Этапы:

  1. Основная деятельность вызывает getLocalPreferences (this) -> это будет читать ваши настройки, задавать объект фильтра в вашем классе приложения и возвращать его.
  2. Когда вы снова вызываете функцию getLocalPreferences() где-то еще в приложении, она сначала проверяет, не является ли это в классе приложения намного быстрее.

ПРИМЕЧАНИЕ: ВСЕГДА проверьте, если широкая переменная приложения отличается от NULL, причина ->http://www.developerphil.com/dont-store-data-in-the-application-object/

Объект приложения не останется в памяти навсегда, это будет убить. Вопреки распространенному мнению, приложение не будет перезапущено с нуля. Android создаст новый объект приложения и начнет действие, в котором пользователь раньше был, чтобы представить иллюзию, что приложение никогда не было убито в первую очередь.

Если я не проверял на нуль я позволил бы NullPointer быть выброшен при вызове, например getMaxDistance() на объекте фильтра (если объект приложения был прокатывается из памяти на Android)