Сохраняем настройки
Введение
Очень часто требуется сохранить какие-то настройки. Простой пример — девочка с удовольствим пользовалась нашей программой Счётчик ворон и вертела головой в поисках очередной птицы. Но вот незадача — когда девочка вечером закрывала своё приложение, то на следующий день счётчик обнулялся. А так хотелось узнать, сколько ворон можно насчитать за месяц. Но однажды в дождливый осенний день она встретила рыжее чудо. Впрочем, это уже совсем другая история.
Самый простой способ, который приходит в голову — сбросить данные в файл, а при запуске приложения считывать необходимые данные из файла. Второй вариант — работать с базой данных и хранить настройки там.
Рассмотрим сначала второй вариант. Хранить данные в базе данных не всегда оправдано, если данных не слишком много, они простые и нам не нужно анализировать данные на предмет, сколько мышек поймал котик в прошлом году и сколько часов он спал в январе и феврале.
Первый вариант с файлами хорош, например, для сохранения больших текстовых заметок. Естественно, и для простых данных мы тоже можем использовать файлы. Записали что-то в файл, а потом открыли его и считали данные.
На самом деле нет необходимости изобретать свой велосипед и придумывать свою структуру для хранения данных. В Android существует класс SharedPreferences (Общие настройки), разработанный специально для этих целей. Приложение автоматически создаёт файл в своей папке и хранит простые данные в виде «ключ — значение». Весь процесс создания, открытия, чтения файла оптимизирован и избавляет вас от головной боли.
Общие настройки поддерживают базовые типы boolean, String, float, long и int, что делает их идеальным средством для быстрого сохранения значений по умолчанию, переменных экземпляра класса, текущего состояния UI и пользовательских настроек. Они чаще всего используются для обеспечения постоянства данных между пользовательскими сессиями и доступа к ним компонентов приложения.
Сохранение значений параметров
Если у вас сохранился старый проект по подсчёту ворон, то можете снова его открыть и добавить новый код. Либо заново создайте проект по памяти, заодно проверите, как усвоили урок и сможете ли вы самостоятельно создать проект с нуля, не заглядывая на сайт за подсказкой.
Создаём переменную, представляющую экземпляр класса SharedPreferences, который отвечает за работу с настройками:
Вы передаёте в указанный метод getSharedPreferences() имя вашего файла (файл будет создан автоматически) и стандартное разрешение по умолчанию MODE_PRIVATE — только приложение имеет доступ к настройкам. Если вам необходимо, вы можете создать несколько подобных файлов настроек.
Немного опередим события и представим, что приложение запущено, и мы считаем ворон весь день. Когда мы закрываем приложение, то нам необходимо сохранить информацию в «Общих настройках». Обычно для этих целей используют методы onPause() или onStop().
Чтобы внести изменения в настройки, нужно использовать класс SharedPreferences.Editor. Получить объект Editor можно через вызов метода edit() объекта SharedPreferences. После того, как вы внесли все необходимые изменения, вызовите метод apply(), чтобы изменения вступили в силу.
Теперь при закрытии программы значение счётчика автоматически запишется в файл. При повторном запуске приложения нам уже не нужно инициализировать счётчик со значением 0. Мы можем прочитать сохранённое значение и использовать его для счётчика, чтобы продолжить подсчёт. Сделаем это в методе onResume().
Мы проверяем сначала наличие ключа APP_PREFERENCES_COUNTER, а затем извлекаем из ключа его значение.
Вот и всё. Небольшие изменения в коде сделали программу продвинутой. Теперь вы можете спокойно закрывать и открывать программу, ваши данные не будут потеряны. При желании вы можете добавить кнопку для сброса счётчика. Это вам в качестве домашнего задания.
В теории показаны дополнительные примеры и даны подробные сведения об использовании «Общих настроек». Вам следует хорошенько разобраться в этом механизме, так как он часто используется на практике. Более того, некоторые программисты предпочитают использовать «Общие настройки» вместо базы данных, если это позволяет логика программы, так как это работает быстрее и потребляет меньше ресурсов. Выбор за вами.
Вместо послесловия
Не волнуйтесь, с котёнком всё в порядке. Девочка подобрала его и принесла домой. И добрая девочка по-прежнему пользуется нашей программой «Счётчик ворон». Наверное, биологом станет или ветеринаром.
При написании статьи использовались иллюстрации Рины З..
Источник
Настройки и состояние приложения
Сохранение состояния приложения
В одной из предыдущих тем был рассмотрен жизненный цикл Activity в приложении на Android, где после создания Activity вызывался метод onRestoreInstanceState , который восстанавливал ее состояние, а перед завершением работы вызывался метод onSaveInstanceState , который сохранял состояние Actiity. Оба этих метода в качестве параметра принимают объект Bundle , который как раз и хранит состояние activity:
В какой ситуации могут быть уместно использование подобных методов? Банальная ситуация — переворот экрана и переход от портретной ориентации к альбомной и наоборот. Если, к примеру, графический интерфейс содержит текстовое поле для вывода TextView, и мы программно изменяем его текст, то после изменения ориентации экрана его текст может исчезнуть. Или если у нас глобальные переменные, то при изменении ориентации экрана их значения могут быть сброшены до значений по умолчанию.
Чтобы точнее понять проблему, с которой мы можем столкнуться, рассмотрим пример. Изменим файл activity_main следующим образом:
Здесь определено поле EditText, в которое вводим имя. И также определена кнопка для его сохранения.
Далее для вывода сохраненного имени предназначено поле TextView, а для получения сохраненного имени — вторая кнопка.
Теперь изменим класс MainActivity :
Для хранения имени в программе определена переменная name. При нажатии на первую кнопку сохраняем текст из EditText в переменную name, а при нажатии на вторую кнопку — обратно получаем текст из переменной name в поле TextView.
Запустим приложение введем какое-нибудь имя, сохраним и получим его в TextView:
Но если мы перейдем к альбомному режиму, то TextView окажется пустым, несмотря на то, что в него вроде бы уже получили нужное значение:
И даже если мы попробуем заново получить значение из переменной name, то мы увидим, что она обнулилась:
Чтобы избежать подобных ситуаций как раз и следует сохранять и восстанавливать состояние activity. Для этого изменим код MainActivity:
В методе onSaveInstanceState() сохраняем состояние. Для этого вызываем у параметра Bundle метод putString(key, value) , первый параметр которого — ключ, а второй — значение сохраняемых данных. В данном случае мы сохраняем строку, поэтому вызываем метод putString() . Для сохранения объектов других типов данных мы можем вызвать соответствующий метод:
put() : универсальный метод, который добавляет значение типа Object. Соответственно поле получения данное значение необходимо преобразовать к нужному типу
putString() : добавляет объект типа String
putInt() : добавляет значение типа int
putByte() : добавляет значение типа byte
putChar() : добавляет значение типа char
putShort() : добавляет значение типа short
putLong() : добавляет значение типа long
putFloat() : добавляет значение типа float
putDouble() : добавляет значение типа double
putBoolean() : добавляет значение типа boolean
putCharArray() : добавляет массив объектов char
putIntArray() : добавляет массив объектов int
putFloatArray() : добавляет массив объектов float
putSerializable() : добавляет объект интерфейса Serializable
putParcelable() : добавляет объект Parcelable
Каждый такой метод также в качестве первого параметра принимает ключа, а в качестве второго — значение.
В методе onRestoreInstanceState происходит обратный процесс — с помощью метода getString(key) по ключу получаем из сохраненного состояния строку по ключу. Соответственно для получения данных других типов мы можем использовать аналогичные методы:
get() : универсальный метод, который возвращает значение типа Object. Соответственно поле получения данное значение необходимо преобразовать к нужному типу
getString() : возвращает объект типа String
getInt() : возвращает значение типа int
getByte() : возвращает значение типа byte
getChar() : возвращает значение типа char
getShort() : возвращает значение типа short
getLong() : возвращает значение типа long
getFloat() : возвращает значение типа float
getDouble() : возвращает значение типа double
getBoolean() : возвращает значение типа boolean
getCharArray() : возвращает массив объектов char
getIntArray() : возвращает массив объектов int
getFloatArray() : возвращает массив объектов float
getSerializable() : возвращает объект интерфейса Serializable
getParcelable() : возвращает объект Parcelable
Для примера рассмотрим сохранение-получение более сложных данных. Например, объектов определенного класса. Пусть у нас есть класс User :
Класс User реализует интерфейс Serializable , поэтому мы можем сохранить его объекты с помощью метода putSerializable() , а получить с помощью метода getSerializable() .
Пусть у нас будет следующий интерфейс в activity_main.xml :
Здесь определены два поля ввода для имени и возраста соответственно.
В классе MainActivity пропишем логику сохранения и получения данных:
Здесь также сохраняем данные в переменную User, которая предварительно инициализированна некоторыми данными по умолчанию. А при нажатии на кнопку получения получем данные из переменной и передаем их для вывода в текстовое поле.
Источник
Сохранение данных при закрытии приложения и извлечение этих данных
Я знаю, есть много вопросов относительно сохранения / получения данных здесь. Я делал поиск вещей самостоятельно и действительно думал, что смогу найти ответы, не требуя «задать вопрос», но я начал задаваться вопросом, что я не видел здесь ответа.
Естественно, я делаю приложение. При закрытии приложения я хочу сохранить простой массив чисел (0 или 1) или логических значений. После запуска приложения я хочу найти этот массив, если он существует, и получить его для использования в приложении.
Я начал размещать свой код в активности, в которой будет использоваться массив. Но я начал задаваться вопросом, нужно ли мне копировать / вставлять переопределенную onStop() во все мои действия? Или я делаю это в основной деятельности и каким-то образом свяжу другие действия.
В принципе, независимо от того, какое состояние / активность приложение в настоящий момент включено, когда приложение закрыто, я хочу сохранить массив int / bool и открыть его обратно при запуске приложения.
Может быть, я не знал, как искать то, что я хотел, поэтому объяснять это было правильно.
Я не против делать больше поиска, но если кто-то укажет на меня в правильном направлении, по крайней мере, я буду очень благодарен.
EDIT: Если есть лучший способ сделать то, что я хочу, чем то, что я описал (например, используя другое состояние вместо onStop() ), пожалуйста, не стесняйтесь выкидывать идеи. Это мой первый раз, когда мне приходится иметь дело с жизненными циклами действий, и я немного смущен даже после изучения учебников по разработке Android. Я действительно думаю, что они плохо сделаны в большинстве случаев.
Когда вашему приложению необходимо сохранить некоторые постоянные данные, вы всегда должны делать это в onPause() и вместо onStop() . Потому что, если ОС Android onStop() ваш процесс, то onStop() и onDestroy() никогда не вызываются. Аналогичным образом извлекаются данные в onResume() .
Рассматривая цель, которую вы хотите выполнить, SharedPreferences – это все, что вы хотите.
В документации указано:
«SharePreferences предоставляет общую структуру, которая позволяет сохранять и извлекать постоянные пары значений ключа для примитивных типов данных. Вы можете использовать SharedPreferences для сохранения любых примитивных данных: booleans, floats, ints, longs и strings. Эти данные будут сохраняться у пользователя Сессий (даже если ваше приложение убито) ».
Используйте SharedPreference для хранения небольшого объема данных или используйте SQLite для хранения большого объема данных. См. Эту ссылку
Сериализуйте объект и передавайте его, что более надежно, чем общие настройки (было много проблем с согласованностью с общими предпочтениями):
Затем используйте класс для использования:
Затем везде, где вы хотите написать переменную, используйте:
Источник
Полный список
— хранение данных с помощью Preferences
Хватит об Intent и Activity. Поговорим о хранении данных. В Android есть несколько способов хранения данных:
Preferences — в качестве аналогии можно привести виндовые INI-файлы
SQLite — база данных, таблицы
обычные файлы — внутренние и внешние (на SD карте)
Мы начнем с самого простого – Preferences. Значения сохраняются в виде пары: имя, значение. Так же, как и например extras в Intent.
Разработаем приложение. В нем будет поле для ввода текста и две кнопки – Save и Load. По нажатию на Save мы будем сохранять значение из поля, по нажатию на Load – загружать.
Project name: P0331_SharedPreferences
Build Target: Android 2.3.3
Application name: SharedPreferences
Package name: ru.startandroid.develop.p0331sharedpreferences
Create Activity: MainActivity
Откроем main.xml и создадим такой экран:
Поле ввода и две кнопки.
Теперь пишем код в MainActivity.java:
Определение элементов экрана, присвоение обработчиков и реализация onClick – тут все понятно и как обычно. Нам интересны методы, которые мы вызываем в onClick
saveText – сохранение данных. Сначала с помощью метода getPreferences получаем объект sPref класса SharedPreferences, который позволяет работать с данными (читать и писать). Константа MODE_PRIVATE используется для настройки доступа и означает, что после сохранения, данные будут видны только этому приложению. Далее, чтобы редактировать данные, необходим объект Editor – получаем его из sPref. В метод putString указываем наименование переменной – это константа SAVED_TEXT, и значение – содержимое поля etText. Чтобы данные сохранились, необходимо выполнить commit. И для наглядности выводим сообщение, что данные сохранены.
loadText – загрузка данных. Так же, как и saveText, с помощью метода getPreferences получаем объект sPref класса SharedPreferences. MODE_PRIVATE снова указывается, хотя и используется только при записи данных. Здесь Editor мы не используем, т.к. нас интересует только чтение данных. Читаем с помощью метода getString – в параметрах указываем константу — это имя, и значение по умолчанию (пустая строка). Далее пишем значение в поле ввода etText и выводим сообщение, что данные считаны.
Все сохраняем, запускаем приложение.
Для начала, давайте убедимся, что сохранение в принципе нужно. Введите какой-нить текст в поле ввода
и не нажимая кнопку Save закройте приложение кнопкой Назад.
Теперь найдите приложение в общем списке приложений эмулятора
и запустите снова.
Поле ввода пустое. То, что мы вводили – пропало при закрытии программы. Нажатие на Load тоже ничего не даст – мы ничего не сохраняли.
Давайте попробуем сохранять. Снова введите значение и нажмите Save.
Значение сохранилось в системе.
Теперь закроем приложение (Назад), снова откроем и нажмем Load. Значение считалось и отобразилось.
Давайте сделаем так, чтобы сохранение и загрузка происходили автоматически при закрытии и открытии приложения и не надо было жать кнопки. Для этого метод loadText будем вызывать в onCreate.
(Добавляете только строку 8)
а метод saveText — в onDestroy
Все сохраним, запустим. Теперь можно вводить данные, закрывать приложение, снова открывать и данные не потеряются. Кнопки Save и Load также работают. В какой момент сохранять данные в ваших приложениях – решать только вам. По нажатию кнопки, при закрытии программы или еще по какому-либо событию. Главное – теперь вы это умеете.
Еще немного слов по этой теме.
Preferences-данные сохраняются в файлы и вы можете посмотреть их. Для этого в Eclipse откройте меню Window > Show View > Other и выберите Android > File Explorer. Отобразилась файловая система эмулятора.
Открываем папку data/data/ru.startandroid.develop.p0331sharedpreferences/shared_prefs и видим там файл MainActivity.xml. Если его выгрузить на комп и открыть — увидим следующее:
Все верно, имя — saved_text и значение — abcdefg.
Обратите внимание, что в пути к файлу используется наш package.
Теперь разберемся, откуда взялось наименование файла MainActivity.xml. Кроме метода getPreferences, который мы использовали, есть метод getSharedPreferences. Он выполняет абсолютно те же функции, но позволяет указать имя файла для хранения данных. Т.е., например, если бы мы в saveText использовали для получение SharedPreferences такой код:
То данные сохранились бы в файле MyPref.xml, а не в MainActivity.xml.
Теперь если мы посмотрим исходники метода getPreferences, то видим следующее:
Используется метод getSharedPreferences, а в качестве имени файла берется имя класса текущего Activity. Отсюда и появилось имя файла MainActivity.xml.
— используете getPreferences, если работаете с данными для текущего Activity и не хотите выдумывать имя файла.
— используете getSharedPreferences, если сохраняете, например, данные — общие для нескольких Activity и сами выбираете имя файла для сохранения.
Кстати, в File Explorer вы можете видеть юниксовые rwx-права доступа к файлу. Попробуйте при сохранении данных использовать не MODE_PRIVATE, а MODE_WORLD_READABLE или MODE_WORLD_WRITEABLE и посмотрите, как будут меняться права.
Полный код MainActivity.java:
На следующем уроке:
— хранение данных с помощью SQLite
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Источник