Следует ли осуществлять доступ к SharedPreferences из потока пользовательского интерфейса? - PullRequest
106 голосов
/ 07 декабря 2010

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

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

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

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

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

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

Ответы [ 4 ]

176 голосов
/ 07 декабря 2010

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

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

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

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

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

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

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

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

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

5 голосов
/ 07 декабря 2010

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

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

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

4 голосов
/ 11 ноября 2014

Одна тонкость в ответе Брэда: даже если вы загрузите 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 () представляется безопасным для потока переднего плана.

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

1 голос
/ 20 мая 2015

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

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 создаст новый объект Application и запустит действие, в котором пользователь находился раньше, чтобы создать иллюзию, что приложение никогда не было убито.

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

...