Android studio сохранение данных при закрытии

Сохраняем настройки

Введение

Очень часто требуется сохранить какие-то настройки. Простой пример — девочка с удовольствим пользовалась нашей программой Счётчик ворон и вертела головой в поисках очередной птицы. Но вот незадача — когда девочка вечером закрывала своё приложение, то на следующий день счётчик обнулялся. А так хотелось узнать, сколько ворон можно насчитать за месяц. Но однажды в дождливый осенний день она встретила рыжее чудо. Впрочем, это уже совсем другая история.

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

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

Первый вариант с файлами хорош, например, для сохранения больших текстовых заметок. Естественно, и для простых данных мы тоже можем использовать файлы. Записали что-то в файл, а потом открыли его и считали данные.

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

Вот и всё. Небольшие изменения в коде сделали программу продвинутой. Теперь вы можете спокойно закрывать и открывать программу, ваши данные не будут потеряны. При желании вы можете добавить кнопку для сброса счётчика. Это вам в качестве домашнего задания.

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

Читайте также:  Android studio actionbar toolbar

Вместо послесловия

Не волнуйтесь, с котёнком всё в порядке. Девочка подобрала его и принесла домой. И добрая девочка по-прежнему пользуется нашей программой «Счётчик ворон». Наверное, биологом станет или ветеринаром.

При написании статьи использовались иллюстрации Рины З..

Источник

Android studio сохранение данных при закрытии

В этом уроке мы научимся выполнять сохранение данных нашего приложения для того, чтобы при следующих его запусках воссоздать сохраненное, а не вводить заново. Самый, наверное, распространенный и полезный способ от такого сохранения — это сохранение логина и пароля к чему либо: аккаунту, архиву и многое другое. Хотя на паролях все возможности далеко не исчерпываются, но мы рассмотрим пример использования стандартного интерфейса Shared Preferences на примере сохранения текстовых данных. Мы сделаем Android приложение, в котором будет два поля для ввода логина и пароля (любых двух слов) и две кнопки, сохранить и загрузить. Если заполнить поля ввода, но не нажать кнопку сохранить — введенные данные будут теряться, если же перед выходом пользователь нажмет на кнопку «Сохранить» то при следующем запуске он нажмет кнопку «Загрузить» и поля автоматически заполнятся введенными и сохраненными ранее символами.

Сохранение данных с помощью Shared Preferences происходит благодаря тому, что при сохранении данных таким образом, приложение создает специальный xml файл, где будет храниться нужная информация, и при нужде получить сохраненные данные, программа будет обращаться к этому xml файлу. Файл храниться в системе устройства по адресу /data/data/НАЗВАНИЕ_ПРОЕКТА/shared_prefs.

Интерфейс для сохранения настроек Shared Preferences довольно прост в использовании. Для этого вызывается специальный метод под названием getPreferences(). По отношению к введенным данным можно выбрать 3 уровня доступности:

MODE_PRIVATE — только это приложение может читать настройки с xml файла;

MODE_WORLD_READABLE — все приложения могут читать с xml файла;

MODE_WORLD_WRITEABLE — все приложения могут выполнять запись в xml файл.

После объявления метода работы с Shared Preferences нужно создать объект Editor, который нужен для создания пар имя-значение, которые будут записаны в xml файл для сохранения с помощью метода put() . Для успешного внесения данных в файл сохранения в конце нужно выполнить команду commit.

Для того, чтобы извлечь сохраненные данные, нужно обратиться к ним с помощью команды get (), ссылаясь на необходимые пары-значения.

Итак, начнем делать наше приложение, описанное вначале. Создаем новый проект, выбираем Blank Activity. Интерфейс программы будет иметь два поля для ввода текста и две кнопки. Открываем файл activity_main.xml и добавляем в него следующее:

Теперь открываем MainActivity.java и добавляем этот код (объяснения там же):

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

Теперь осталось только протестировать наше приложение:

Похоже на чудо, но оно работает!

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

Также можете переходить к изучению SQLite Database.

Источник

Настройки и состояние приложения

Сохранение состояния приложения

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

Источник

Cохранение состояний в android приложениях

Сегодня я хотел поделиться с вами еще одним подходом сохранения состояния при разработке android приложений. Не для кого не секрет, что наше приложение в фоне может быть убито в любой момент и эта проблема становится все актуальнее с вводом агрессивного энергосбережения – привет Oreo. Также никто не отменял смену конфигурации на телефоне: ориентация, смена языка и т.д. И чтобы открыть приложение из бэкграунда и отобразить интерфейс в последнем состоянии нам нужно позаботиться о его сохранении. Ох уж этот onSaveInstanceState.

Читайте также:  Определитель телефонного номера для андроида

Сколько боли он нам принес.

Далее я буду приводить примеры, ипользуя Clean Achitecture и Dagger2, так что будьте готовы к этому:)

Вопрос сохранения состояния в зависимости от задач можно решить несколькими способами:

  1. Сохранять первичные данные в onSaveInstanceState хоста (Activity, Fragment) — такие как айдишник страницы, пользователя, да что угодно. То, что нам требуется для первичного получения данных и отображения страницы.
  2. Сохранять полученные данные в интеракторе в репозитории (SharedPreference, Database.
  3. Использовать ретеин фрагменты для сохранения и восстановления данных при пересоздании активити.

Но что делать, если нам нужно восстановить состояние ui, а также текущую реакцию интерфейса на действие пользователя? Для большей простоты рассмотрим решение этой задачи на реальном примере. У нас есть страница логина — пользователь вводит свои данные, нажимает на кнопку и тут к нему поступает входящий звонок. Наше приложение уходит в бэкграунд. Его убивает система. Звучит страшновато, не правда ли?)

Пользователь возвращается к приложению и что он должен увидеть? Как минимум, продолжение операции логина и показ прогресса. Если приложение успело пройти логин до вызова метода onDestroy хоста, то тогда пользователь увидит навигацию на стартовый экран приложения. Данное поведение можно с легкостью решить, используя паттерн состояния (State machine). Очень хороший доклад от яндекс. В этой же статье постараюсь поделиться пережеванными мыслями по этому докладу.

Теперь немного кода:

В нашем случае state owner будет презентер.

Рассматривая страницу логина можно выделить три уникальных состояния:

LoginInitState, LoginProgressingState, LoginCompleteState.

Итак, рассмотрим теперь, что происходит в этих состояниях.

LoginInitState у нас происходит валидация полей и в случае успешной валидации кнопка login становится активной.

В LoginProgressingState делается запрос логина, сохраняется токен, делаются дополнительные запросы для старта главной активити приложения.

В LoginCompleteState осуществляется навигация на главный экран приложения.

Условно переход между состояниями можно отобразить на следующей диаграмме:

Выход из состояния LoginProgressingState происходит в случае успешной операции логина в состояние LoginCompleteState, а в случае сбоя в LoginInitState. Таким образом, когда у нас вьюха детачится, мы имеем вполне детерменированное состояние презентера. Это состояние мы должны сохранить, используя стандартный механизм андроида onSaveInstanceState. Для того, чтобы мы могли это сделать, все состояния логина должны имплементировать интерфейс Parcelable. Поэтому расширяем наш базовый интерфейс BaseState.

Далее у нас встает вопрос, как пробросить это состояние из презентера в наш хост? Самый простой способ — из хоста попросить данные у презентера, но с точки зрения архитектуры это выглядит не очень. И поэтому нам на помощь приходят retain фрагменты. Мы можем создать интерфейс для кэша и имплементировать его в таком фрагменте:

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

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

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

Источник

Оцените статью