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

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

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

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

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

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

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


person cottonBallPaws    schedule 06.12.2010    source источник
comment
Я всегда делал свои предпочтения операций в потоке пользовательского интерфейса. Хотя я думаю, что это имеет смысл, так как это операция ввода-вывода   -  person Falmarri    schedule 07.12.2010


Ответы (6)


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

На что обратить внимание: (в ленивой маркированной форме)

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

А вот по поводу загрузки...

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

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

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

  • но если вам все равно не нужны ваши настройки во время onCreate(), это время загрузки излишне задерживает запуск вашего приложения, поэтому, как правило, лучше иметь что-то вроде подкласса FutureTask‹SharedPreferences›, который запускает новый поток для .set () значение подклассов FutureTask. Затем просто найдите участника FutureTask‹SharedPreferences›, когда вам это нужно, и .get(). Я планирую сделать это бесплатным за кулисами в Honeycomb, прозрачно. Я постараюсь выпустить пример кода, демонстрирующий передовой опыт в этой области.

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

person Brad Fitzpatrick    schedule 06.12.2010
comment
Ничего себе, не ожидал получить такой четкий ответ прямо из источника! Большое спасибо! - person cottonBallPaws; 07.12.2010
comment
Для новых читателей этого замечательного поста найдите ниже ссылку на упомянутый выше пост в блоге @Brad Fitzpatrick: запись Брэда в блоге разработчиков Android о строгом режиме . В посте также есть ссылка на пример кода для использования apply (начиная с пряников) или commit (froyo) на основе версии Android для хранения общих настроек: [условно используйте apply или commit] (code.google.com/p/zippy-android/source/browse/trunk/examples/< /а>) - person tony m; 18.05.2013
comment
Это все еще актуально для ICS\JB? - person ekatz; 06.06.2013

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

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

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

person mreichelt    schedule 06.12.2010
comment
Но разве SQLite не является также файлом, который нужно читать из флэш-памяти, но он больше и сложнее по сравнению с файлом настроек. Я предполагал, что для объема данных, связанных с настройками, файл настроек будет намного быстрее, чем база данных SQLite. - person Tom; 12.10.2012
comment
Это правильно. Как уже упоминал Брэд, это почти всегда не проблема, и он также упоминает, что хорошей идеей является загрузка SharedPreferences один раз (возможно, даже в потоке с использованием FutureTask) и удержание его для любого возможного доступа к одному экземпляру. - person mreichelt; 22.10.2012

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

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

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

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

person Tom O'Neill    schedule 11.11.2014
comment
Да, это определенно соображение. Однако достаточно часто, если вы вызываете get*(), то вы находитесь на этапе, когда вам нужно результирующее значение для продолжения, и в этом случае вам все равно придется ждать, и вы не получите выгоду от вызова get*() в другом потоке. Просто мысль. - person pallgeuer; 20.11.2020

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

Класс приложения:

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 (активность, которая вызывается первой в вашем приложении):

LocalPreference.getLocalPreferences(this);

Объяснение шагов:

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

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

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

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

person Jdruwe    schedule 20.05.2015

Класс SharedPreferences выполняет некоторые операции чтения и записи в файлах XML на диске, поэтому, как и любая другая операция ввода-вывода, он может блокироваться. Объем данных, хранящихся в настоящее время в SharedPreferences, влияет на время и ресурсы, потребляемые вызовами API. Для минимальных объемов данных требуется несколько миллисекунд (иногда даже меньше миллисекунды), чтобы получить/поместить данные. Но с точки зрения эксперта может быть важно повысить производительность, выполняя вызовы API в фоновом режиме. Для асинхронных SharedPreferences я предлагаю проверить библиотеку Datum.

person navid    schedule 10.08.2020

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

person j2emanue    schedule 26.08.2020