жизненный цикл android приложения
Введение в разработку Android-приложений
1.6. Компоненты Android-приложения
1.6.1. Activities
Activity представляет собой пользовательский интерфейс для одного действия, которое пользователь может совершить. Например, приложение для обмена текстовыми сообщениями может иметь одно Activity для отображения списка контактов, другое – для написания сообщения выбранному контакту, третье – для просмотра сообщений и еще одно для изменения настроек. Все эти Activities формируют единый пользовательский интерфейс, но не зависят друг от друга.
Приложение может состоять из одного Activity или из нескольких. Это зависит от типа приложения и его дизайна. Одно Activity может вызвать другое. Каждое Activity задает окно для отображения, которое, обычно, занимает весь экран, но может быть меньше и плавать поверх других окон. Activity может использовать дополнительные окна, например, всплывающий диалог, который требует промежуточного ответа пользователя, или окно, которое отображает пользователям важную информацию при выборе элемента, заслуживающего особого внимания.
Визуальный интерфейс строится на основе иерархии визуальных компонентов, производных от базового класса View. Android имеет ряд готовых к использованию компонентов, а также кнопки, текстовые поля, полосы прокрутки, меню, флажки и многие другие.
Activity может находиться в одном из трех состояний:
Если Activity приостановлено или остановлено, система может удалить его из памяти, либо послать запрос на его завершение, или просто уничтожить его процесс. Когда Activity снова отображается пользователю, его состояние полностью восстанавливается.
Переходя от состояния к состоянию, Activity уведомляет об этом, вызывая следующие методы:
Жизненный цикл Activity состоит из трех вложенных циклов (Рис. 1.3):
За ним всегда следует вызов onResume()
1.6.2. Типы процессов в Android-приложении
Жизненный цикл приложения тесно связан с жизненным циклом его процесса. Также он зависит от текущего состояния системы. В случае нехватки памяти, Android убивает наименее значимые процессы. Значимость процесса зависит от его типа. Типы процессов, в зависимости от важности, выглядят следующим образом (от наиболее до наименее важных):
1.6.3. Services
Service – это некий процесс, который запускается в фоновом режиме. Как пример, Service может получать данные по сети, выполнять какие-либо длительные вычисления. Хорошим примером Service служит проигрыватель музыки. Пользователь может выбрать любую песню в проигрывателе, включить ее и закрыть плеер занявшись чем-нибудь другим. Музыка будет проигрываться в фоновом процессе. Service проигрывания музыки будет работать, даже если Activity плеера закрыта.
Подобно Activity, Service имеет свои методы жизненного цикла:
В полном жизненном цикле Service существует два вложенных цикла:
Как и Activities, Services запускаются в главном потоке процесса приложения. По этой причине их следует запускать в отдельном потоке, чтобы они не блокировали другие компоненты или пользовательский интерфейс.
1.6.4. Broadcast receivers
Broadcast receiver – это компонент, который ничего не делает, кроме того, что рассылает и реагирует на широковещательные сообщения. Примером широковещательных компонентов могут быть: сообщения об переходе на летнее/зимнее время, сообщения об минимальном заряде батареи и так далее.
Broadcast receiver не отображает пользовательский интерфейс, но может запустить Activity на полученное сообщение или использовать NotificationManager для привлечения внимания пользователя. Привлечь внимание пользователя можно, например, вибрацией устройства, проигрыванием звука или миганием вспышки.
1.6.5. Content providers
Content providers предоставляют доступ к данным (чтение, добавление, обновление). Content provider может предоставлять доступ к данным не только своему приложению, но и другим. Данные могут размещаться в файловой системе, в базе данных.
Нестыдные вопросы про жизненный цикл
Каждый разработчик сталкивался с вопросами про жизненный цикл Activity: что такое bind-сервис, как сохранить состояние интерфейса при повороте экрана и чем Fragment отличается от Activity.
У нас в FunCorp накопился список вопросов на похожие темы, но с определёнными нюансами. Некоторыми из них я и хочу с вами поделиться.
1. Все знают, что если открыть второе активити поверх первого и повернуть экран, то цепочка вызовов жизненного цикла будет выглядеть следующим образом:
FirstActivity: onPause
SecondActivity: onCreate
SecondActivity: onStart
SecondActivity: onResume
FirstActivity: onSaveInstanceState
FirstActivity: onStop
SecondActivity: onPause
SecondActivity: onSaveInstanceState
SecondActivity: onStop
SecondActivity: onCreate
SecondActivity: onStart
SecondActivity: onRestoreInstanceState
SecondActivity: onResume
SecondActivity: onPause
FirstActivity: onCreate
FirstActivity: onStart
FirstActivity: onRestoreInstanceState
SecondActivity: onStop
А что будет в случае, если второе активити прозрачное?
Решение
В случае с прозрачным верхним активити с точки зрения логики всё немного отличается. Именно потому, что оно прозрачное, после поворота необходимо восстановить содержимое и того активити, которое находится непосредственно под ним. Поэтому порядок вызовов будет немного отличаться:
FirstActivity: onPause
SecondActivity: onCreate
SecondActivity: onStart
SecondActivity: onResume
SecondActivity: onPause
SecondActivity: onSaveInstanceState
SecondActivity: onStop
SecondActivity: onCreate
SecondActivity: onStart
SecondActivity: onRestoreInstanceState
SecondActivity: onResume
FirstActivity: onSaveInstanceState
FirstActivity: onStop
FirstActivity: onCreate
FirstActivity: onStart
FirstActivity: onRestoreInstanceState
FirstActivity: onResume
FirstActivity: onPause
2. Ни одно приложение не обходится без динамического добавления вью, но иногда приходится перемещать одну и ту же вью между разными экранами. Можно ли один и тот же объект добавить одновременно в два разных активити? Что будет, если я создам её с контекстом Application и захочу добавлять одновременно в различные активити?
Зачем это нужно?
Существуют «не очень приятные» библиотеки, которые внутри кастомных вью держат важную бизнес-логику, и пересоздание этих вью в рамках каждого нового активити является плохим решением, т.к. хочется иметь один набор данных.
Решение
Ничего не мешает создать вью с контекстом Application. Она просто применит дефолтные стили, не относящиеся к какому-либо активити. Также без проблем можно перемещать эту вью между разными активити, но нужно следить, чтобы она была добавлена только в одного родителя
Можно, например, подписаться на ActivityLifecycleCallbacks, на onStop удалять (removeView) из текущего активити, на onStart добавлять в следующее открываемое (addView).
3. Фрагмент можно добавить через add и через replace. А в чём отличие между этими двумя вариантами с точки зрения порядка вызова методов жизненного цикла? В чём преимущества каждого из них?
Решение
Даже если вы добавляете фрагмент через replace, то это не значит, что он полностью заменяется. Это значит, что на этом месте в контейнере заменится его вью, следовательно, у текущего фрагмента будет вызвано onDestroyView, а при возврате назад будет снова вызван onCreateView.
Это довольно сильно меняет правила игры. Приходится детачить все контроллеры и классы, связанные с UI именно в onDestroyView. Нужно чётко разделять получение данных, необходимых фрагменту, и заполнение вью (списков и т.д.), так как заполнение и разрушение вью будет происходить намного чаще, чем получение данных (чтение каких-то данных из БД).
Также появляются нюансы с восстановлениям состояния: например, onSaveInstanceState иногда приходит после onDestroyView. К тому же стоит учитывать, что если в onViewStateRestored пришёл null, то это значит, что не нужно ничего восстанавливать, а не сбрасываться до дефолтного состояния.
Если говорить про удобства между add и replace, то replace экономнее по памяти, если у вас глубокая навигация (у нас глубина навигации юзера — один из продуктовых KPI). Также намного удобнее с replace управлять панелью инструментов, так как в onCreateView можно её переинфлейтить. Из плюсов add: меньше проблем с жизненным циклом, при возврате назад не пересоздаются вью и не нужно ничего заново заполнять.
4. Иногда всё ещё приходится работать напрямую с сервисами и даже с bind-сервисами. С одним из подобных сервисов взаимодействует активити (только один активити). Он коннектится к сервису и передаёт в него данные. При повороте экрана наш активити разрушается, и мы обязаны отбайндится от этого сервиса. Но если нет ни одного соединения, то сервис разрушается, и после поворота bind будет к совершенно другому сервису. Как сделать так, чтобы при повороте сервис оставался жить?
Решение
Если вы знаете красивое решение, то напишите в комментариях. На ум приходит только нечто подобное:
5. Недавно мы переделали навигацию внутри нашего приложения на Single Activity (с помощью одной из доступных библиотек). Раньше каждый экран приложения был отдельным активити, сейчас навигация работает на фрагментах. Проблема возврата к активити в середине стека решалась intent-флагами. Как можно вернуться к фрагменту в середине стека?
Решение
Да, решения из коробки FragmentManager не предоставляет. Cicerone делает внутри себя нечто подобное:
6. Также недавно мы избавились от такого неэффективного и сложного компонента, как ViewPager, потому что логика взаимодействия с ним очень сложна, а поведение фрагментов непрогнозируемо в определённых кейсах. В некоторых фрагментах мы использовали Inner-фрагменты. Что будет при использовании фрагментов внутри элементов RecycleView?
Решение
В общем случае не будет ничего плохого. Фрагмент без проблем добавится и будет отображаться. Единственное, с чем мы столкнулись, — это нестыковки с его жизненным циклом. Реализация на ViewPager управляет жизненным циклом фрагментов посредством setUserVisibleHint, а RecycleView делает всё в лоб, не думая про фактическую видимость и доступность фрагментов.
7. Всё по той же причине перехода с ViewPager мы столкнулись с проблемой восстановления состояния. В случае с фрагментами это реализовывалось силами фреймворка: в нужных местах мы просто переопределяли onSaveInstanceState и сохраняли в Bundle все необходимые данные. При пересоздании ViewPager все фрагменты восстанавливались силами FragmentManager и возвращали свое состояние. Что делать в случае с RecycleView и его ViewHolder?
Решение
«Надо писать всё в базу и каждый раз читать из неё», — скажете вы. Или логика сохранения состояния должна быть снаружи, а список — это просто отображение. В идеальном мире так и есть. Но в нашем случае каждый элемент списка — это сложный экран со своей логикой. Поэтому пришлось изобрести свой велосипед в стиле «сделаем такую же логику, как во ViewPager и фрагменте»:
На Fragment.onSaveInstanceState мы считываем состояние нужных нам холдеров и кладём их в Bundle. При пересоздании холдеров мы достаем сохранённый Bundle и на onBindViewHolder передаём найденные состояния внутрь холдеров:
8. Чем нам это грозит?
Решение
На самом деле, ничего плохого в этом нет. В том же RecycleView хранятся списки из элементов с одинаковыми id. Однако всё-таки есть небольшой нюанс:
Стоит быть внимательнее, если у нас в иерархии есть элементы с одинаковыми id, т.к. возвращается всегда именно первый найденный элемент, и на разных уровнях вызова findViewById это могут быть разные объекты.
9. Вы падаете с TooLargeTransaction при повороте экрана (да, здесь по-прежнему косвенно виноват наш ViewPager). Как найти виновного?
Решение
Ниже пример, как мы достаём состояние фрагментов из Bundle:
Далее мы просто вычисляем размер Bundle и логируем его:
10. Предположим, у нас есть активити и набор зависимостей на нём. При определённых условиях нам нужно пересоздать набор этих зависимостей (например, по клику запустить какой-то эксперимент с другим UI). Как нам это реализовать?
Решение
Конечно, можно повозиться с флагами и сделать это каким-то «костыльным» перезапуском активити через запуск интента. Но на деле всё очень просто — у активити есть метод recreate.
Скорее всего, большая часть этих знаний вам и не пригодится, так как к каждому из них приходишь не от хорошей жизни. Однако некоторые из них хорошо демонстрируют, как человек умеет рассуждать и предлагать свои решения. Мы используем подобные вопросы на собеседованиях. Если у вас есть интересные задачи, которые вам предлагали решить на собеседованиях, или вы сами их ставите, напишите их в комментариях — интересно будет обсудить!
Полный список
— Activity LifeCycle – поведение Activity при создании, вызове, закрытии
Теория
При работе приложения, мы создаем новые Activity и закрываем старые, сворачиваем приложение, снова открываем и т.д. Activity умеет обрабатывать все эти движения. Это необходимо, например, для освобождения ресурсов или сохранения данных. В хелпе достаточно подробно это описано.
Созданное при работе приложения Activity может быть в одном из трех состояний:
Когда Activity переходит из одного состояния в другое, система вызывает различные его методы, которые мы можем заполнять своим кодом. Схематично это можно изобразить так:
Для упрощения понимания я дал краткое описание состояний в скобках под названиями. А крестом обозначил отсутствие Activity.
Итак, мы имеем следующие методы Activity, которые вызывает система:
onCreate() – вызывается при первом создании Activity
onStart() – вызывается перед тем, как Activity будет видно пользователю
onResume() – вызывается перед тем как будет доступно для активности пользователя (взаимодействие)
onPause() – вызывается перед тем, как будет показано другое Activity
onStop() – вызывается когда Activity становится не видно пользователю
onDestroy() – вызывается перед тем, как Activity будет уничтожено
Т.е. эти методы НЕ вызывают смену состояния. Наоборот, смена состояния Activity является триггером, который вызывает эти методы. Тем самым нас уведомляют о смене, и мы можем реагировать соответственно. Посмотрим на практике, когда и в каком порядке вызываются эти методы.
Практика
В этом уроке нам надо будет эмулировать событие смены ориентации экрана. Но эмулятор с Android 2.3 делает это криво, поэтому в проекте будем использовать версию 2.2. Для этого надо создать новое AVD по версии 2.2
Создадим проект (обратите внимание, используем Android 2.2.):
Project name: P0231_OneActivityState
Build Target: Android 2.2
Application name: OneActivityState
Package name: ru.startandroid.develop.p0231oneactivitystate
Create Activity: MainActivity
Layout не меняем, нам он сейчас не важен. Открываем MainActivity.java, там как обычно код по умолчанию:
Мы видим, что реализован уже знакомый нам по схеме метод onCreate. Повторюсь, важно понимать, что этот метод НЕ создает Activity. Создание – это дело системы. Т.е. система сама создает Activity, а нам дает возможность немного поучаствовать и выполнить свой код в методе onCreate(). Мы этой возможностью пользуемся и говорим системе, что Activity должна отобразить экран из R.layout.main.
Добавим все остальные методы из схемы, и в каждый добавим запись в лог.
В каментах подсказали важное замечание! При реализации этих методов обязательно вызывайте соответствующие методы супер-класса и обязательно перед вашим кодом. См. код выше. Каждый метод содержит вызов метода супер-класса и свой код расположен после этих вызовов.
Теперь, когда методы будут вызываться, мы будем видеть это в логах. Настроим фильтр на тег «States», чтобы не искать свои сообщения в общей куче логов. Как это делается мы проходили в уроке 12
Все сохраним и запустим приложение. После того, как запустилось, смотрим лог:
MainActivity: onCreate()
MainActivity: onStart()
MainActivity: onResume()
Теперь нажмем кнопку Back на эмуляторе. Activity закрылось. Смотрим лог:
MainActivity: onPause()
MainActivity: onStop()
MainActivity: onDestroy()
Activity проделывает путь, обратный созданию. Сначала теряет фокус (onPause), затем исчезает с экрана (onStop), затем полностью уничтожается (onDestroy).
Смена ориентации экрана
Посмотрим, как ведет себя Activity, когда происходит смена ориентации экрана. Запустите снова приложение (либо найдите его в списке приложений в системе на эмуляторе, либо снова нажмите CTRL+F11 в Eclipse ). В логах снова отобразились три метода, вызванные при создании. Теперь в эмуляторе нажмите CTRL+F12, ориентация сменилась. Кажется, что ничего особенного не произошло, но смотрим логи и видим:
MainActivity: onPause()
MainActivity: onStop()
MainActivity: onDestroy()
MainActivity: onCreate()
MainActivity: onStart()
MainActivity: onResume()
Activity полностью уничтожается и снова создается. При этом обычно выполняются процедуры сохранения и восстановления данных, чтобы не потерялись данные, и приложение сохранило свой вид. Про то, как это делается, мы будем говорить в последующих уроках.
Также есть еще метод onRestart. Он вызывается перед методом onStart, если Activity не создается с нуля, а восстанавливается из состояния Stoped. Его мы рассмотрим в следующем уроке.
Обычно в учебниках эта тема дается по-другому. Но мне это шаблонное объяснение кажется недостаточно понятным, поэтому я написал свое. Как всегда, надеюсь, что у меня получилось раскрыть тему )
Советую вам после этого урока прочитать хелп, ссылку на который я дал в самом начале урока. Там все очень хорошо написано. И знания лучше усвоятся. Пока что, главное – это понять в какой момент, какой метод вызывается. А уже дальше мы будем разбираться, как это можно использовать и что там кодить.
На следующем уроке:
— изучаем смену состояния на примере двух Activity
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Жизненный цикл Android приложения
Мы знаем, как ведут себя приложения на таких платформах как Windows, Linux или Mac. После запуска программы мы можем свернуть и развернуть ее тогда, когда это потребуется, при этом программа полностью сохранит свое состояние (конечно, при условии, что не произойдет системный сбой). Если говорить о Android, то здесь ситуация совершенно иная и мы в значительно меньшей степени можем контролировать протекаение жизни приложения. Поэтому, прежде чем приступать к процессу разработки, мы должны полностью знать и понимать жизненный цикл Activity в Android приложениях.
Activity может находиться в 4-х состояниях, описанных ниже:
Activity находится в данном состоянии, когда находится на экране устройства, полностью видна пользователю и имеет на себе фокус.
В состоянии паузы Activity частично видна пользователю, но не имеет фокуса и не является активной. Это состояние возникает, когда текущее Activity перекрывается другой, не закрывающей весь кран или имеющей прозрачность. Приостановленная Activity полностью «жива», но может быть выгружена системой при нехватке памяти, если освободить память другим способом не удается.
Activity переходит в это состояние, когда долго невидна на экране. Другая Activity находится поверх и полностью закрывает ее. В состоянии остановки Activity также остается
«живой», но имеет больше шансов быть выгруженной при нехватке памяти.
Activity считается уничтоженной, если она больше не существует в памяти устройства. Либо Activity попросту еще не была запущена или прекратила свою работу по описанным выше причинам.
Диаграмма жизненного цикла Android приложения
На рисунке ниже представлена диаграмма, демонстрирующая то, как протекает жизненный цикл Activity в Android приложениях. Эта информация является важной для разработчика, поэтому внимательно изучите диаграмму.
В серых прямоугольниках показаны callback методы, которые вызываются прежде, чем Activity перейдет в какое-либо состояние.
Данную диаграмму демонстрирующую протекание жизненного цикла Android приложения, можно описать следующим образом:
Метод onPause() будет вызван в том случае, когда система собирается вернуть на передний план другую Activity или когда пользователю необходимо перейти к другим частям системы. Это последний метод, который гарантировано будет вызван до того, как Activity может быть уничтожена системой. Иными словами, существует вероятность того, что в состоянии паузы Activity может быть уничтожена системой без предварительного вызова каких-либо других методов. Поэтому очень важно при вызове данного метода сохранить пользовательские настройки и важные данные.
По умолчанию Activity может оставаться в состоянии паузы если:
Есть три возможных варианта выхода Activity из состояния паузы:
Циклы жизненного цикла Android Activity
Анализируя диаграмму работы Android Activity, мы можем выделить три цикла и их колбэк методы, существующие для каждой Activity.
Жизненный цикл Android
Понимание жизненного цикла Android и изменения его состояния имеет решающее значение для создания приложений с меньшим количеством ошибок, использующих меньше ресурсов и обеспечивающих хорошее взаимодействие с пользователем.
Когда дело доходит до создания приложения на Android, активити и фрагменты являются ключевыми компонентами для создания пользовательского интерфейса (UI). Когда пользователь перемещается по приложению, эти компоненты проходят через разные состояния жизненного цикла Android.
Понимание жизненного цикла и правильная реакция на изменения его состояния имеют решающее значение. Он позволяет создавать приложения с меньшим количеством ошибок, использовать меньше системных ресурсов Android и обеспечивать хорошее взаимодействие с пользователем.
В этом туториале вы познакомитесь с простым приложением PuppyCounter, которое можно использовать для подсчета собак во время прогулки по окрестностям. Вы узнаете:
Для этого урока вам понадобится Android Studio и Android-устройство или эмулятор.
Начнем
Для начала загрузите материалы. Откройте стартовый проект в Android Studio. Как только проект откроется, дайте ему запуститься и синхронизироваться, и вы будете готовы к работе!
Запустите приложение и проверьте его возможности:
В приложении два экрана:
Затем ознакомьтесь со структурой проекта:
Прежде чем вдаваться в подробности жизненного цикла активити, рассмотрим некоторую предысторию роли жизненного цикла в приложениях Android.
Понимание роли жизненного цикла приложений
ОС Android использует иерархию по важности, чтобы определить, какие процессы оставить в живых или уничтожить. В этой иерархии процессы делятся на разные типы. Этот тип зависит от запущенных в данный момент компонентов приложения и их текущего состояния.
Разработчики должны понимать, как различные компоненты влияют на время жизни процесса. Неправильное использование этих компонентов может привести к тому, что система остановит процесс, пока выполняет важную работу.
Изучение жизненного цикла активности
На рисунке выше показаны различные состояния, через которые происходит активность в течение своего жизненного цикла:
Обратите внимание на разные колбэки (обратные вызовы) между состояниями. ОС вызывает эти колбэки, когда активити переходит из одного состояния в другое. Вы можете переопределить эти методы в своих активити, чтобы выполнять задачи в ответ на эти изменения состояния жизненного цикла.
Всякий раз, когда вы переопределяете такой колбэк, убедитесь, что вы также вызываете метод суперкласса. Если вы этого не сделаете, ваше приложение не выполнит какую-то важную работу и может вылететь или зависнуть.
Скомпилируйте и запустите приложение. Затем проверьте логи. Чтобы просмотреть логи в Android Studio, откройте инструмент Logcat, щелкнув Logcat внизу на странице. Введите PuppyCounter в историю поиска, чтобы показать результаты:
Затем закройте приложение, нажав назад, или проведите пальцем в обратном направлении, если у вас включена навигация с помощью жестов. Еще раз проверьте логи. Вы должны увидеть что-то вроде этого:
Заметка
В логах вы не увидите сообщение «Back button clicked» (Нажата кнопка назад). Мы добавили это в изображение, чтобы вам было легче заметить нажатия кнопок.
Понимание колбэков жизненного цикла активити
Вы только что прошли один полный жизненный цикл активити: активити была создана, возобновлена и окончательно уничтожена, когда вы вышли из приложения.
На приведенной выше диаграмме представлен жизненный цикл активити:
Бывают ситуации, когда система убивает процесс, следовательно, не вызывает onDestroy() или любые другие методы жизненного цикла активити. Таким образом, его не следует использовать для того, чтобы делать те вещи, которые должны остаться после завершения процесса.
Заметка
Дополнительные сведения о жизненном цикле Activity смотрите в Android Developer documentation.
Сохранение и восстановление состояния экземпляра активити
Если вы немного поиграли с приложением, то возможно, заметили пару ошибок. Счетчик увеличивается при нажатии карточек на главном экране.
Теперь поверните устройство, чтобы изменить ориентацию экрана. Если на дисплее вашего устройства включен автоповорот, вы увидите что-то вроде этого:
Состояние счетчика обнулилось при смене ориентации экрана. Посмотрим логи:
Вы можете видеть, что когда поменялась ориентация экрана, приложение уничтожило активити в портретной ориентации, а затем создало и возобновило новую активити в горизонтальной ориентации. Поскольку в MainActivity.kt у вас нет никакой логики для сохранения и восстановления состояния счетчика, действие было потеряно во время этого процесса.
Скоро мы это исправим!
Сохранение состояния экземпляра
Откройте MainActivity.kt и добавьте следующий код:
Заметка
Восстановление состояния
Прекрасно! Теперь у вас есть логика для сохранения состояния, но в ней нет пользы, пока у вас нет логики для ее получения. В MainActivity.kt добавьте следующий код ниже onSaveInstanceState() :
Заметка
Запустите приложение. Увеличьте значения счетчиков и поменяйте ориентацию экрана:
Также проверьте логи, когда все колбеки будут вызваны, вы увидите следующую картину:
Заметка
Не путайте onSaveInstanceState() и onRestoreInstanceState() с колбеками жизненного цикла активности. ОС вызывает эти методы только в тот момент, когда это нужно.
Отлично! Теперь, когда вы исправили ошибку в приложении, пора перейти к следующей. 🙂
Передача данных между экранами
Увеличьте значения счетчиков на главном экране, а затем откройте экран «Share». Вы заметите, что значения экрана «Share» не совпадают со значениями на главном экране.
В MainActivity.kt измените showShareScreen() следующим образом:
В ShareActivity.kt добавьте следующий метод:
Чтобы завершить логику получения, вызовите этот метод в onCreate() в ShareActivity.kt:
Супер! Запустите приложение. Увеличьте значения счетчиков и откройте экран «Share». Вы увидите что-то вроде этого:
Проверьте логи, чтобы увидеть жизненные циклы активити при переходе от одного экрана к другому.
Разбираем данные объекта Intent
Данные, которые вы передаете с помощью Intent при запуске новой активити, сохраняются при воссоздании активити.
Чтобы завершить этот раздел, нажмите назад, когда экран находится в горизонтальной ориентации, и еще раз просмотрите логи.
Отлично! Теперь, когда вы понимаете жизненный цикл активности и то, как правильно управлять состоянием активити, пора переходить к фрагментам. 🙂
Изучение жизненного цикла фрагмента
Как и у активити, у фрагментов есть свой жизненный цикл. Когда пользователь перемещается по вашему приложению и взаимодействует с ним, ваши фрагменты переходят из одного состояния в другое в своем жизненном цикле, когда они добавляются, удаляются, а также выходят на экран или выходят из него.
На рисунке выше вы можете видеть, что жизненный цикл фрагмента аналогичен жизненному циклу активити, но содержит некоторые дополнительные методы, специфичные для фрагмента. Прежде чем объяснять каждый колбэк, проверьте их в приложении.
В предыдущем разделе вы поиграли с двумя активити и увидели, как меняется их жизненный цикл при перемещении между экранами. В этом примере вы реализуете те же экраны с фрагментами. Вы можете найти два фрагмента, которые представляют каждый экран в пакете фрагментов: MainFragment.kt и ShareFragment.kt. Также есть одна активити контейнера и пакет viewmodels. Пока не обращайте внимания на пакет viewmodels. Он понадобится вам в следующем разделе.
Если вы проверите MainFragment.kt, вы заметите много общего с MainActivity.kt. У них одинаковая логика управления состояниями, но MainFragment.kt содержит еще несколько колбэков жизненного цикла.
Отлично! Теперь соберите и запустите приложение. Затем осмотрите логи.
Обратите внимание, как жизненный цикл фрагмента синхронизируется с жизненным циклом активности. Сначала приложение создает и запускает ActivityWithFragments. После этого он создает и запускает фрагмент и его просмотр. Наконец, он возобновляет как активити, так и фрагмент.
Далее нажмите назад и снова наблюдайте за логами.
Закрыв приложение, вы запустили процесс уничтожения активити. Как и раньше, события жизненного цикла фрагмента следуют за событиями жизненного цикла активити. И активити, и фрагмент сначала приостанавливаются, затем останавливаются и, наконец, уничтожаются.
Понимание обратных вызовов жизненного цикла фрагмента
Теперь вы можете немного глубже погрузиться в каждое событие жизненного цикла, чтобы лучше понять жизненный цикл фрагмента:
Теперь, когда вы лучше понимаете, что скрывается под капотом, переходите между основным экраном и экраном Share, чтобы увидеть танец жизненного цикла фрагмента. 🙂
Как вы видели в этом и предыдущем разделе, жизненный цикл Android довольно сложен. Управлять состояниями и взаимодействовать с пользовательским интерфейсом в нужное время может быть непросто для неопытных разработчиков. Это привело к появлению некоторых новых API и компонентов Android, которые должны облегчить жизнь всем разработчикам Android. Одним из таких компонентов является ViewModel.
Использование ViewModel для хранения данных пользовательского интерфейса
ViewModel предназначен для хранения данных, связанных с пользовательским интерфейсом, и управления ими с учетом жизненного цикла.
В пакете viewmodels создайте новый класс с именем MainViewModel.kt.
В MainFragment.kt добавьте следующее:
Добавьте следующую строку выше onCreate() :
Затем добавьте в MainFragment.kt следующий метод:
Скомпилируйте и запустите приложение. Коснитесь счетчиков пару раз и поверните экран. Вы должны увидеть что-то вроде этого:
На приведенном ниже рисунке можно увидеть время жизни ViewModel рядом с соответствующим жизненным циклом действия.
- жидкий пластик для заливки форм своими руками
- жизненный цикл андроид приложения