Onsaveinstancestate in fragment android

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

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

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

Когда работа 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 для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме

Источник

Сохранение состояния фрагментов (Fragment)

Распространенной проблемой является некорректное поведение приложения при повороте девайса. Дело в том что при повороте Activity-host (Activity которое является родителем для фрагмента) уничтожается. В тот момент когда этот процесс происходит FragmentManager отвечает за уничтожение дочернего фрагмента. FragmentManager запускает методы угасающего жизненного цикла фрагмента: onPause(), onStop() и onDestroy().

В случае если в контроллере нашего дочернего фрагмента, к примеру, есть объект Media-Player, то в методе фрагмента Fragment.onDestroy() экземпляр нашего звонко играющего Media-Player-а прервет воспроизведение медиа данных. Первое, что приходит в голову, сохранить состояние объекта Media-Player вызвав Fragment.onSaveInstanceState(Bundle), что сохранит данные, а новое Activity загрузит их. Однако, сохранение состояния объекта MediaPlayer и его последующее восстановление, все равно, прерывает воспроизведение и заставляет акул ненависти сновать в головах пользователей.

Сохранение Fragments

Благо, у Fragment присутствует механизм, при помощи которого экземпляр Media-Player может “пережить” изменение конфигурации. Переопределив метод Fragment.onCreate(. ) и задав свойство фрагмента.

Свойство retainInstance фрагмента, по дефолту, является false. Это означает, что при поворотах девайса Fragment не сохраняется, а уничтожается и создается по новому вместе с Activity-host. При вызове setRetainInstance(true) фрагмент не уничтожается вместе с его хостом и передается новому Activity в не измененном виде. При сохранении фрагмента можно рассчитывать на то, что все его поля (включая View) сохранят прежние значения. Мы к ним обращаемся, а они уже есть и все. Используя этот подход можно убедится в том, что при повороте девайса наш объект MediaPlayer не прервет свое воспроизведение и пользователь не будет психовать.

Повороты и сохраненные фрагменты

Теперь стоит взглянуть более подробно на то, как работают сохраненные фрагменты. Fragment руководствуется тем обстоятельством, что представление фрагмента может уничтожаться и создаваться заново без необходимости уничтожать сам Fragment. При изменении конфигурации FragmentManager уничтожает и заново собирает представление фрагментов. Аналогично ведет себя и Activity. Это происходит из соображений что в новой конфигурации могут потребоваться новые ресурсы. На случай, если для нового варианта существуют более подходящие ресурсы, представление строится «с нуля».

Читайте также:  Pics art для андроид

FragmentManager проверяет свойство retainInstance каждого фрагмента. Если оно дефолтное (false), FragmentManager уничтожает экземпляр фрагмента. Fragment и его представление будут созданы заново новым экземпляром FragmentManager принадлежащем новой активности.

Что же происходит если значение retainInstance равно true. Представление фрагмента уничтожается но сам фрагмент остается. Создастся новый экземпляр Activity, а затем и новый FragmentManager который найдет сохраненный Fragment и воссоздаст его View.

Наш сохраненный фрагмент открепляется (detached) от предыдущей Activity и продолжает жить но уже не имея Activity-host.

Соответственно, переход в сохраненное состояние происходит при выполнении следующих условий:

  • для фрагмента был вызван метод setRetainInstance(true)
  • Activity-host уничтожается для изменения конфигурации (обычно это поворот девайса)

В этом случае Fragment живет совсем недолго от момента отсоединения от своей первой Activity до передачи его в пользование к немедленно созданной нового Acticvity.

Сохранение фрагментов: действительно так хорошо?

Сохраненные фрагменты: ведь правда, удобно? Да! Действительно удобно. На первый взгляд они решают все проблемы, возникающие с уничтожением Activity и фрагментов при поворотах. При изменении конфигурации устройства для подбора наиболее подходящих ресурсов создается новое представление, а в вашем распоряжении имеется простой способ сохранения данных и объектов.

В таком случае возникает вопрос, почему не сохранять все фрагменты подряд и почему фрагменты не сохраняются по умолчанию? Невольно складывается впечатление, что Android без энтузиазма относится к сохранению Fragment-ов в UI. Мне не ясно, почему это так.
Стоит отметить, что сохраненный Fragment продолжает существовать только при уничтожении Activity — при изменения конфигурации. Если Activity уничтожается из-за того, что ОС потребовалось освободить память, то все сохраненные фрагменты также будут уничтожены.

Пора поговорить и об onSaveInstanceState(Bundle)

Такой подход является более распространенным в борьбе с проблемой потери данных при поворотах. Если ваше преложение с легкостью отрабатывает эту ситуацию то все благодаря работе поведения onSaveInstanceState(. ) по дефолту.

Метод onSaveInstanceState(. ) проектировался для решения сохранения и восстановления состояния пользовательского интерфейса приложения. Как я думаю, Вы догадались — есть основополагающее различие между этими подходами. Главное отличие между переопределением Fragment.onSaveInstanceState(. ) и сохранением Fragment-а — продолжительность существования сохраненных данных.

В том случае, если вы преследуете цель сохранить данные на то мгновение пока происходит изменение конфигурации, сохранение фрагмента потребует существенно меньшей работы.Это особенно справедливо при сохранении объект- разработчику не нужно беспокоиться о том, реализует ли объект Serializable.

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

Источник

Fragment (Фрагменты). Часть пятая

Сохранение данных

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

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

Кажется, я поторопился со своим утверждением.

Новый проект создавать не будем, а модифицируем старый. Заодно закрепим материал.

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

Повторяем прошлые шаги. Создаём новый класс, наследуемся от Fragment.

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

Подключаем разметки к соответствующим фрагментам в методе onCreateView().

Читайте также:  Build failure android unity

Для второго фрагмента напишите код самостоятельно.

Подключаем фрагменты в разметке основной активности activity_main.xml:

Не копируйте код с сайта, проделывайте операции самостоятельно через редактор в режиме Design и Text, как это объяснялось в предыдущих примерах.

Подключим кнопку в первом фрагменте.

Во втором фрагменте подключим компонент TextView и добавим метод для вывода текста.

Если мы вызовем метод changeText() с каким-нибудь текстом, то текст отобразится в TextView. Наша задача — научить фрагменты обмениваться данными через активность.

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

Напоминаю, мы не связываем два фрагмента между собой напрямую. Создаём интерфейс для этих целей.

В предыдущем примере мы создавали интерфейс внутри первого фрагмента. Для разнообразия изменим подход. Создадим новый класс через New | Java Class и в диалоговом окне для поля Kind выберем Interface. Также зададим ему имя Communicator.

Интерфейс будет состоять из одного метода.

Активность должна реализовать интерфейс.

Фрагмент с кнопкой может использовать объект интерфейса для отправки данных. Инициализируем его в новом методе onActivityCreated(), который ранее не использовали. Метод сработает, когда активность будет создана и готова к работе.

При щелчках на кнопках мы вызываем метод count(), которому передаём нужную информацию.

А сам метод в активности будет выглядеть следующим образом:

Фрагмент передаёт через метод count() данные data, а активность их принимает и передаёт их в метод второго фрагмента changeText().

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

Если вы создавали пример на основе предыдущих уроков, то не поворачивайте экран. Сначала удалите (или переименуйте) файлы для альбомной ориентации, чтобы не получить ошибку и крах приложения.

Теперь переходим непосредственно к теме нашего урока. Повернув экран, мы обнаружим, что данные из текстового блока пропали. Наши подсчёты — коту под хвост!

Мы можем в новой ориентации начать новый подсчёт, но повернув устройство обратно в портретный режим, снова потеряем данные.

Мы знаем, что при поворотах активность создаётся заново. Поэтому все данные сбрасываются. Чтобы сохранить данные, у фрагментов есть соответствующие методы, схожие с подобными методами у активностей. Задействуем их.

Метод onSaveInstanceState() поможет нам. Добавим метод в первый фрагмент с кнопкой.

Параметр метода outState является объектом класса Bundle и позволяет хранить различные типы в формате «ключ-значение».

У фрагментов также есть метод onCreate(Bundle savedInstanceState), где используется объект этого же класса Bundle только под другим именем savedInstanceState. Несмотря на разные имена, речь идёт об одном и том же объекте. И сохраняя данные в методе onSaveInstanceState(), мы можем их получить в методе onCreate():

При повороте фрагмент сохранит значение счётчика, перезапустится и восстановит значение счётчика. Если запустить пример, то увидим следующее. Щёлкнем несколько раз по кнопке и повернём экран. Данные сбросятся и мы снова увидим пустой текст. Но стоит нам нажать на кнопку, то увидим, что отсчёт пошёл не сначала, а продолжил со своего последнего значения. Мы видим, что первый фрагмент запоминает свои данные. А второй фрагмент пока тупит. Поможем ему.

Добавим новую переменную mData, которая будет хранить текст сообщения. У метода onCreateView() также есть параметр savedInstanceState класса Bundle, позволяющий извлечь сохранённые данные.

А текст мы сохраним в методе onSaveInstanceState(). В методе changeText() добавим строку кода, чтобы текст брался из новой переменной. Теперь при первом запуске всё работает как прежде. При повороте текст из mData сохраняется в методе putString() и восстанавливается через getString().

После этих изменений программа больше не теряет своих данных и можно спокойно считать котов. Я стал смотреть на гифку и считать. Через 8 минут 16 секунд последний кот выпрыгнул в окно и в итоге получилось 154578 котов. Перепроверьте.

Источник

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