Сохранение состояния activity android kotlin

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

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

В одной из предыдущих тем был рассмотрен жизненный цикл 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

Читайте также:  Microsoft office для android apk

getIntArray() : возвращает массив объектов int

getFloatArray() : возвращает массив объектов float

getSerializable() : возвращает объект интерфейса Serializable

getParcelable() : возвращает объект Parcelable

Для примера рассмотрим сохранение-получение более сложных данных. Например, объектов определенного класса. Пусть у нас есть класс User :

Класс User реализует интерфейс Serializable , поэтому мы можем сохранить его объекты с помощью метода putSerializable() , а получить с помощью метода getSerializable() .

Пусть у нас будет следующий интерфейс в activity_main.xml :

Здесь определены два поля ввода для имени и возраста соответственно.

В классе MainActivity пропишем логику сохранения и получения данных:

Здесь также сохраняем данные в переменную User, которая предварительно инициализированна некоторыми данными по умолчанию. А при нажатии на кнопку получения получем данные из переменной и передаем их для вывода в текстовое поле.

Источник

Урок 6. AndroidDev на Kotlin. Activity Lifecycle. Интерфейс LifecycleObserver

Продолжаем курс по обучению основам разработки мобильных приложений в Android Studio на языке Kotlin.

Это урок 6, в котором мы познакомимся с событиями и состояниями жизненного цикла Активити, а также поговорим о том, как их нужно (и как не нужно) обрабатывать в процессе разработки приложения.

Предыдущий урок, на котором мы добавляли второй экран в андроид-приложение, здесь

Технологии и инструменты, используемые в уроке:

Как вы уже знаете из прошлых уроков, Активити — это, по сути, экран нашего приложения. Каждый пользователь андроид устройства запускает, сворачивает экраны приложения, запускает другие приложения, вертит устройство в руках. Что происходит при этом с активити? Как изменяются его состояния? И главный вопрос, как разработчик приложения должен это учитывать? Сейчас мы это выясним.

Создаем приложение в Android Studio

Давайте создадим приложение и на его примере будем изучать поведение активити.

Каждый раз при создании нового приложения по умолчанию создается класс главного экрана приложения — MainActivity.

Когда пользователь нажимает ярлык приложения, система запускает активити, которое указано как стартовое в манифесте приложения. По умолчанию это MainActivity. После запуска мы видим активити на экране. Мы можем свернуть его или открыть другое приложение, экран настроек и т.д. При этом наше активити уходит на второй план и перестает быть видимым. Однако оно не уничтожается, а продолжает работать в фоне, и мы можем в любой момент к нему вернуться. Наконец, мы можем совсем закрыть приложение, или его может закрыть система, если ей будет не хватать оперативной памяти. Тогда активити прекратит свою работу и будет уничтожено.

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

Схема Lifecycle

А сейчас посмотрите на схему состояний, через которое проходит активити в процессе работы, и событий, которые при этом происходят. Схема с официального сайта https://developer.android.com/images/topic/libraries/architecture/lifecycle-states.png

В прямоугольниках указаны состояния (states) активити. А события (events), которые происходят при смене состояний, отмечены стрелками. Важно понимать, что не события управляют состояниями активити, скорее наоборот. Состояния активити изменяются системой Андроид, а события происходят в процессе изменения.

Когда активити запускается системой, оно инициализируется (INITIALIZED) и происходит событие ON_CREATE. При этом активити переходит к состоянию “создано” (CREATED).В этот момент должен инициализироваться пользовательский интерфейс, поскольку активити готовится отобразиться пользователю. Далее происходит событие ON_START и активити переходит к состоянию “запущено” (STARTED). Следующее событие ON_RESUME. Активити переходит в состояние — RESUMED (возобновлено) — выходит на передний план, получает фокус и отображается пользователю. Если активити в процессе работы теряет фокус и частично перекрывается, например, диалоговым окном или другим активити, то переходит обратно в состояние STARTED. При этом происходит событие ON_PAUSE. В этом состоянии активити приостанавливается, но может быть все еще видимым на экране, например, в многооконном режиме. Если же активити полностью перекрыто, то система его останавливает и переводит в состояние CREATED. Выполняется событие ON_STOP. Активити пока не уничтожается системой и может в любой момент возобновить работу. Но поскольку оно не видно пользователю, то в этом состоянии целесообразно отключать некоторые функции, например, воспроизведение анимации. Если пользователь закрыл активити или система испытывает недостаток памяти, или изменилась конфигурация устройства (произошел поворот), активити уничтожается системой. При этом происходит событие ON_DESTROY. В этот момент необходимо освобождать ресурсы, используемые активити.

Читайте также:  Forkplayer телевизор с андроид

Функции обратного вызова жизненного цикла активити

На рассмотренной нами схеме указаны события жизненного цикла, всего их шесть: ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP и ON_DESTROY..

Для обработки событий класс Activity имеет колбеки — функции обратного вызова, соответствующие каждому событию жизненного цикла. Например, это уже известная вам функция onCreate(savedInstanceState: Bundle?) <…>в которой происходит инициализация пользовательского интерфейса и вызовы некоторых других функций.

Но кроме одноименных функций жизненного цикла, в активити есть несколько дополнительных, таких как onPostResume() и onRestart(). Мы их будем использовать в качестве вспомогательных для большей наглядности.

Во всех функциях обратного вызова нужно переопределять одноименные методы суперкласса во избежание ошибок.

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

onCreate()

Первая функция, которая переопределена по умолчанию в каждом активити, отвечает событию ON_CREATE. Выполняется перед стартом активити, когда активити переходит из состояния INITIALIZED к состоянию CREATED.

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

Источник

Полный список

— сохраняем данные при повороте экрана

Теорию по этому вопросу можно почитать тут. Я здесь вкратце дам вольный перевод.

Когда работа Activity приостанавливается(onPause или onStop), она остается в памяти и хранит все свои объекты и их значения. И при возврате в Activity, все остается, как было. Но если приостановленное Activity уничтожается, например, при нехватке памяти, то соответственно удаляются и все его объекты. И если к нему снова вернуться, то системе надо заново его создавать и восстанавливать данные, которые были утеряны при уничтожении. Для этих целей Activity предоставляет нам для реализации пару методов: первый позволяет сохранить данные – onSaveInstanceState, а второй – восстановить — onRestoreInstanceState.

Эти методы используются в случаях, когда Activity уничтожается, но есть вероятность, что оно еще будет востребовано в своем текущем состоянии. Т.е. при нехватке памяти или при повороте экрана. Если же вы просто нажали кнопку Back (назад) и тем самым явно сами закрыли Activity, то эти методы не будут выполнены.

Но даже если не реализовать эти методы, у них есть реализация по умолчанию, которая сохранит и восстановит данные в экранных компонентах. Это выполняется для всех экранных компонентов, у которых есть ID.

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

Т.к. нам надо будет поворачивать экран, используйте при разработке Android 2.2. В AVD с версией 2.3 поворот глючит.

Project name: P0701_SaveInstanceState
Build Target: Android 2.2
Application name: SaveInstanceState
Package name: ru.startandroid.develop.p0701saveinstancestate
Create Activity: MainActivity

В strings.xml пропишем тексты:

В main.xml нарисуем кнопку и пару полей для ввода текста:

Обратите внимание, что второй EditText без ID.

В MainActivity будем вызывать все методы Lifecycle и два вышеописанных:

В каждом из них пишем лог, чтобы отследить последовательность вызовов. Метод onclick пока не реализуем.

Читайте также:  Какая хорошая чистилка для андроид

Все сохраним и запустим. Введем в текстовые поля какие-нить данные:

и повернем экран CTRL+F12.

Данные в первом поле сохранились при повороте, а во втором пропали. Это произошло потому, что дефолтовые методы сохранения/восстановления умеют работать только с компонентами, которые имеют ID. Посмотрим лог.

Эти три метода выполнились при запуске.

Затем мы повернули экран:

onSaveInstanceState
onPause
onStop
onDestroy
onCreate
onStart
onRestoreInstanceState
onResume

Первым делом вызывается onSaveInstanceState, здесь нам надо будет реализовывать сохранение своих данных. Далее идет уничтожение Activity (onPause, onStop, onDestroy) и создание нового onCreate, onStart. И перед onResume вызывается метод восстановления данных – onRestoreInstanceState.

Последовательность мы рассмотрели — сохраняются данные перед onPause, а восстанавливаются перед onResume. Попробуем теперь что-нибудь сохранить и восстановить. У нас на экране есть кнопка, будем по ее нажатию увеличивать счетчик нажатий на единицу и выводить всплывающее сообщение с итоговым кол-вом нажатий. Переменная cnt у нас уже есть. Реализуем onclick:

Повернем эмулятор обратно в вертикальную ориентацию. Запустим приложение, и жмем на кнопку Count. Видим сообщение с кол-вом нажатий. Нажмем еще несколько раз, получим, например 5.

Теперь повернем экран и снова нажмем кнопку.

Мы видим, что счетчик сбросился.

Это произошло потому, что текущий объект Activity был уничтожен и потерял значения всех переменных, в том числе и cnt. При создании нового Activity значение cnt равно 0 и отсчет пошел заново. Давайте это пофиксим. Реализуем метод сохранения onSaveInstanceState:

В объект outState мы пишем значение переменной cnt. Механизм аналогичен помещению данных в Intent.

Метод восстановления onRestoreInstanceState:

Из savedInstanceState вытаскиваем значение и помещаем в переменную cnt. Теперь при уничтожении и воссоздании Activity переменная cnt сохранит свое значение, и наш счетчик продолжит работать.

Проверим. Вернем AVD в вертикальную ориентацию. Все сохраним, запустим приложение. Понажимаем на кнопку, немного накрутим счетчик

и поворачиваем экран.

Жмем снова кнопку

счетчик не сбросился, а продолжил увеличиваться с последней позиции.

Итак, методы onSaveInstanceState и onRestoreInstanceState по дефолту сохраняют данные в экранных компонентах. Если мы реализуем их самостоятельно, то вызываем методы супер-класса и пишем свой код для своих переменных. Ради интереса, можете попробовать убрать вызовы методов суперкласса из onSaveInstanceState и onRestoreInstanceState. Данные в текстовом поле перестанут сохраняться при повороте экрана.

Кроме метода onRestoreInstanceState, доступ к сохраненным данным также можно получить в методе onCreate. На вход ему подается тот же самый Bundle. Если восстанавливать ничего не нужно, он будет = null.

Есть еще один полезный механизм сохранения данных. Android дает нам возможность сохранить ссылку на какой-либо объект и вернуть ее в новый созданный Activity. Для этого существуют методы:

onRetainNonConfigurationInstance – в нем мы сохраняем ссылку, передавая ее на выход (return) метода

Т.е., например, у нас есть какой то объект myObj (класс MyObject) и нам надо сохранить ссылку на него при повороте экрана.

Мы реализуем в Activity метод onRetainNonConfigurationInstance:

Этот метод будет вызван перед уничтожением Activity. От нас требуется дать на выход этому методу наш объект, который надо сохранить.

А, при создании нового Activity, в onCreate (например) мы используем метод getLastNonConfigurationInstance:

Мы получили обратно объект класса Object и привели его к нашему классу MyObject.

На следующем уроке:

— используем Preferences для работы с настройками приложения

Присоединяйтесь к нам в Telegram:

— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование

— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня

— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме

Источник

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