Урок 70. onSaveInstanceState. Сохранение данных Activity при повороте экрана
— сохраняем данные при повороте экрана
Теорию по этому вопросу можно почитать тут. Я здесь вкратце дам вольный перевод.
Когда работа 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 для работы с настройками приложения
Источник
Настройки и состояние приложения
Сохранение состояния приложения
В одной из предыдущих тем был рассмотрен жизненный цикл 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, которая предварительно инициализированна некоторыми данными по умолчанию. А при нажатии на кнопку получения получем данные из переменной и передаем их для вывода в текстовое поле.
Источник
Полный список
— сохраняем данные при повороте экрана
Теорию по этому вопросу можно почитать тут. Я здесь вкратце дам вольный перевод.
Когда работа 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 для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Источник