Должен ли доступ к 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);
действия:
  1. основное действие вызывает getLocalPreferences (this) - > это прочитает ваш настройки, установите объект фильтра в классе приложения и возвращает его.
  2. когда вы снова вызываете функцию getLocalPreferences () где-то еще в приложении, она сначала проверяет, доступна ли она в классе приложения, который намного быстрее.

Примечание: всегда проверяйте, отличается ли широкая переменная приложения от NULL, причина ->http://www.developerphil.com/dont-store-data-in-the-application-object/

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

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