Android: использование SharedPreferences для сохранения настроек

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

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

Введение

Класс SharedPreferences позволяет создавать в приложении именованные ассоциативные массивы типа «ключ — значение», которые могут быть использованы различными компонентами приложения (работая при этом
в контексте одного и того же приложения).

Общие настройки поддерживают базовые типы boolean, string, float, long и integer, что делает их идеальным средством для быстрого сохранения значений по умолчанию, переменных экземпляра класса, текущего состояния UI и пользовательских настроек.

Создание и сохранение настроек

Чтобы создать или изменить Общие настройки, нужно вызвать метод
getSharedPreferences в контексте приложения, передавая имя общих настроек, которые вы хотите изменить. Настройки недоступны для других приложений.

public static String MY_PREF = "MY_PREF";
protected void savePreferences() 
{
   // получить доступ к объекту Editor, чтобы изменить общие настройки.
   SharedPreferences.Editor editor = mySharedPreferences.edit();
   
   // задать новые базовые типы в объекте общих настроек.
   editor.putBoolean("isTrue", true);
   editor.putFloat("floatNumber", 1f);
   editor.putInt("intNumber", 2);
   editor.putLong("longNumber", 3l);
   editor.putString("textValue", "Not Empty");

   editor.commit();
}

Получение общих настроек

Надо передать в getSharedPreferences имя тех общих настроек, доступ к которым хотите получить, и используйте типизированный метод get<тип>, чтобы извлечь сохраненные значения. Передаются ключ и значение по умолчанию (используется случае, если для данного ключа пока что не сохранено никакое значение).

public static String MY_PREF = "MY_PREF";
public void loadPreferences() 
{

  int mode = Activity.MODE_PRIVATE;
  SharedPreferences mySharedPreferences = getSharedPreferences(MY_PREF, mode); 

  boolean isTrue = mySharedPreferences.getBoolean("isTrue", false);
  float lastFloat = mySharedPreferences.getFloat("floatNumber", 0f);
  int wholeNumber = mySharedPreferences.getInt("intNumber", 1);
  long aNumber = mySharedPreferences.getLong("longNumber", 0);
  String stringPreference = mySharedPreferences.getString("textValue", "");
}

Очистка/удаление значения

Для очистки значений используйте методы SharedPreferences.Editor.remove(String key) и SharedPreferences.Editor.clear().

Файл настроек хранится в /data/data/package_name/shared_prefs/имя_файла_настроек.xml. Можно удалить:

File file= new File("/data/data/.../shared_prefs/файл_настроек.xml")
file.delete();

Preferences Framework

В примерах выше происходило обычное сохранение и восстанавлениеи нужных параметров. То есть, создаётся свой xml-файл разметки и размещаются там нужные элементы управления. Но Android для этих целей предоставляет собственный Preferences Framework, с помощью которого можно создавать индивидуальный набор предпочтений и встраивать их в приложения.

То есть, теперь нет необходимости создавать файл разметки и get/set параметров писать самим. Необходимо лишь в отдельном файле перечислить параметры, а Android уже остальное сделает. Предпочтения — это отдельная активность в приложении, вызываемая из активности. Сам Activity расширяется от класса PreferenceActivity. Предпочтения определяются в отдельном XML-файле, где корневым элементом является элемент <PreferenceScreen>, который представляет собой контейнер для предпочтений и может содержать дочерние элементы <PreferenceScreen>. Элемент <PreferenceCategory> также является контейнерным элементом и предназначен для объединения предпочтений в группы.

PreferenceScreen

Необходимо первоначально создать в папке res/xml/ XML-файл ресурсов, как например, settings.xml. Выбирайте тип файла Android XML File:

PreferenceScreen-Select-Wizard

При создании файла в диалоговом окне установите переключатель на Preference и убедитесь, что корневым элементом файла установлен PreferenceScreen:

PreferenceScreen-Select-Wizard

settings.xml

Далее задаём необходимые настройки:

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
   <PreferenceCategory android:title="@string/interface_settings">
      <EditTextPreference
         android:key="pref_size"
	 android:title="@string/interface_settings_pref_size"
	 android:summary="@string/interface_settings_set_new_fsize"
	 android:defaultValue="14"
	 android:dialogTitle="@string/interface_settings_enter_fsize" />

      <ListPreference
	 android:key="pref_lng"
	 android:title="@string/interface_settings_language"
	 android:summary="@string/interface_settings_set_language"
	 android:defaultValue="1"
	 android:entries="@array/lng_list"
	 android:entryValues="@array/entryvalues_lng"
	 android:dialogTitle="@string/interface_settings_check_language" />
   </PreferenceCategory>
</PreferenceScreen>

Мы определили две настройки: размер шрифта, язык приложения. С <EditTextPreference> всё в целом понятно, простое поле. А вот для выбора языка посложнее элемент. Нас тут интересует 2 поля: android:entries и android:entryValues. Из названия, думаю, понятно их назначение.

Определение параметров в string.xml и array.xml

Естественно, необходимо определить все переменные. В string.xml:


<resources>
<string	name="pref_lng">Стиль</string>
    <string	name="pref_size">Размер</string>
    <string name="interface_settings">Настройки интерфейса</string>
    <string name="interface_settings_pref_size">Размер шрифта</string>
    <string name="interface_settings_set_new_fsize">Устанавливает новый размер шрифта</string>
    <string name="interface_settings_enter_fsize">Введите размер шрифта (от 10 до 32)</string>
    <string name="interface_settings_language">Язык приложения</string>
    <string name="interface_settings_set_language">Устанавливает стиль для шрифта</string>
    <string name="interface_settings_check_language">Выберите язык</string>
    <string name="title_activity_settings">Настройки</string>
</resources>

А так же в array.xml:

<resources>

    <string-array name="lng_list">
        <item>Язык аппарата</item>
        <item>English</item>
        <item>Русский</item>
    </string-array>
    <string-array name="entryvalues_lng">
        <item>default</item>
        <item>en</item>
        <item>ru</item>
    </string-array>

</resources>

PreferenceActivity

Для PreferenceActivity автоматически создаётся SettingsActivity. Нам в нём слегка необходимо изменить метод onCreate.

@Override
public void onCreate(Bundle savedInstanceState) 
{
   super.onCreate(savedInstanceState);
   addPreferencesFromResource(R.xml.settings);
}

Метод addPreferencesFromResource() считывает установленные настройки из XML-файла, где хранятся наши ресурсы, и делает их доступными для программы. В результате мы должны увидеть настройки на экране. Если автоматически наше Activity не было зарегестрировано, то добавим в манифесте AndroidManifest.xml:

<activity
   android:name=".SettingsActivity"
   android:label="@string/title_activity_settings" >
   <meta-data
      android:name="android.support.PARENT_ACTIVITY"
      android:value="android.preference.PreferenceActivity" />
</activity>

Ну и напишем код, чтобы при клике на меню, открывалось наше окно с настройками. Для этого определим метод по созданию меню и onCreateOptionsMenu и метод, отслеживающий нажатия по меню onOptionsItemSelected:

public static final int IDM_PREF = 101;

@Override
public boolean onCreateOptionsMenu(Menu menu ) 
{
   menu.add(0,  IDM_PREF, 0, "Настройки");
   return super.onCreateOptionsMenu(menu);
}

@Override 
public boolean onOptionsItemSelected(MenuItem item) 
{
   Intent intent = new Intent();
   switch (item.getItemId())
   {
      case IDM_PREF: 
      intent.setClass(this, SettingsActivity.class); 
      startActivity(intent); 
      break; 
    }
}
Preference Screen

Теперь при вызове меню опций, нажимайте на пункт “Настройки” и откроется наше окно настроек.

Для сохранения предпочтений на выбор есть 4 класса:

  • CheckBoxPreference
  • EditTextPreference
  • ListPreference
  • RingtonePreference

Конкретно в данном примере используется EditTextPreference и ListPreference.

Так выглядит окно изменения для настроек:

preference screen - select languagepreference screen - font

Отслеживание изменений в общих настройках

Класс onSharedPreferenceChangeListener позволяет вызвать callback-метод в момент добавления, удаления или изменения конкретной Общей настройки. Используя этот обработчик, компоненты вашего приложения могут следить за изменениями в настройках, установленных пользователем, и обновлять пользовательский интерфейс или корректировать поведение программы.

This function is not relevant for a modern fragment-based PreferenceActivity

This function is not relevant for a modern fragment-based PreferenceActivity. Inflates the given XML resource and adds the preference hierarchy to the current preference hierarchy.

Такое сообщение вы увидите в SDK по поводу многих методов PreferenceActivity, у того же addPreferencesFromResource. Это означает, что методы устарели. В сущности, ничего фатального в этом нет, просто в более поздних версиях появились более удобные методы.

С версии 3.0 появились так называемые Fragments, с помощью которых можно работать с настройками.

Ещё раз повторю, если у вас старый вариант для работы с настройками используется, то он будет работать на всех устройствах нормально. Но если вы пишите для устройств с версией Android 3+, то лучше используйте фрагменты.

  Категории: Android, Коддинг
  • Pingback: Android: значения по умолчанию для SharedPreferences | Suvitruf's Blog()

  • Pingback: Взлом игр для Android на примере Defender | Suvitruf's Blog()

  • Pingback: libGDX: Часть 3. Обзор модулей | Suvitruf's Blog()

  • booaky

    спасибо

  • Игорь

    Спасибо. А почему вы сохраняете общие настройки через интерфейс SharedPreferences.Editor editor = mySharedPreferences.edit();? Можно же сразу через SharedPreferences сохранять, то есть не так editor.putBoolean(“isTrue”, true);, а так mySharedPreferences.edit().putBoolean(“isTrue”, true)..commit(); Чем ваш способ совершеннее, краткостью, или чем то еще? Это имеет значение или нет?

    • http://suvitruf.ru Suvitruf

      Метод edit() каждый раз новый экземпляр класса создаёт. В моём случае он 1 раз создастся и потом после вызова commit() все изменения вступят в силу. А в вашем случае будут лишние затраты на создание объектов и т.п.
      + в моём случае всё это атомарно будет происходить в рамках одной транзакции.

  • Cbyc

    Спасибо, полезная тема.
    Возник вопрос который не освещен.
    SharedPreferences.Editor.clear().
    Например у меня есть счетчик и данные сохраняются.
    Нужно повесить кнопку, для сброса данных. Как можно это осуществить?