Android lifecycle activity and fragment

Содержание
  1. Жизненный цикл фрагментов
  2. FirstFragment
  3. activity_main.xml
  4. MainActivity.java
  5. Дополнительное чтение
  6. The Android Lifecycle cheat sheet — part III : Fragments
  7. Scenario 1: Activity with Fragment starts and finishes
  8. Scenario 2: Activity with Fragment is rotated
  9. State management
  10. Fragments — Scenario 3: Activity with retained Fragment is rotated
  11. Android, жизненый цикл Jetpack компонентов
  12. Начало
  13. Регистрация в spoonacular API
  14. Жизненные циклы в Android
  15. Реагируем на изменения жизненного цикла
  16. Используем компоненты жизненного цикла (Lifecycle-Aware)
  17. Создание наблюдателя жизненного цикла (lifecycle observer)
  18. События и состояния жизненного цикла
  19. Events
  20. Реакция на события жизненного цикла
  21. Состояния (States)
  22. Используем состояния (State) жизненного цикла
  23. Подписка на события жизненного цикла
  24. Кто владеет жизненным циклом?
  25. Использование ProcessLifecycleOwner
  26. Создаем собственного владельца жизненного цикла
  27. Добавляем события
  28. Реакция на события
  29. Тестирование компонентов жизненного цикла
  30. Настройка тестов
  31. Добавляем тесты
  32. LiveData: компонент жизненного цикла
  33. Создание и присваивание переменных LiveData
  34. Наблюдения за изменениями LiveData
  35. Что посмотреть?

Жизненный цикл фрагментов

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

Список дополнительных методов жизненного цикла фрагментов, которых нет у активности:

onAttach(Activity) Вызывается, когда фрагмент связывается с активностью. С этого момента мы можем получить ссылку на активность через метод getActivity() onCreate() В этом методе можно сделать работу, не связанную с интерфейсом. Например, подготовить адаптер. onCreateView(LayoutInflater, ViewGroup, Bundle) Вызывается для создания компонентов внутри фрагмента onViewCreated() Вызывается сразу после onCreateView() onActivityCreated(Bundle) Вызывается, когда отработает метод активности onCreate(), а значит фрагмент может обратиться к компонентам активности onStart() Вызывается, когда фрагмент видим для пользователя и сразу же срабатывает onStart() активности onDestroyView() onResume() Вызываем после onResume() активности Вызывается, когда набор компонентов удаляется из фрагмента onDetach() Вызывается, когда фрагмент отвязывается от активности

Одноимённые с методами активности методы фрагментов выполняют аналогичные функции. К примеру, метод onResume() вызывается, когда фрагмент вновь становится видимым.

Метод onStart() вызывается, когда фрагмент становится видимым после запуска такого же метода в родительской активности.

Фрагмент всегда связан с активностью. Отдельно фрагмент от активности существовать не может.

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

Метод onCreateView() вызывается один раз, когда фрагмент должен загрузить на экран свой интерфейс. В этом методе вы можете «надуть» (inflate) разметку фрагмента через метод inflate() объекта Inflater, который задан в параметре метода. В фрагментах без интерфейса вы можете пропустить надувание.

Метод onActivityCreated() вызывается после метода onCreateView(), когда создаётся активность-хозяйка для фрагмента. Здесь можно объявить объекты, необходимые для Context.

Фрагменты не являются подклассами Context, вам следует использовать метод getActivity(), чтобы получить родительскую активность.

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

FirstFragment

Разметка для первого фрагмента

По аналогии создайте второй фрагмент SecondFragment, заменив тексты и названия идентификаторов там, где это нужно.

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

activity_main.xml

MainActivity.java

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

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

onAttach
onCreate
onCreateView
onActivityCreated
onStart
onResume

Метод onActivityCreated() теперь считается устаревшим.

Нажимаем на кнопку «Home»:

Запустим из списка недавно запущенных программ:

Выходим из программы через кнопку «Back»:

onPause
onStop
onDestroyView
onDestroy
onDetach

При замещении первого фрагмента вторым почти как при закрытии, только нет методов onDestroy и onDetach, а затем повторяются те же методы при старте фрагмента:

// Первый фрагмент onPause
onStop
onDestroyView
// Второй фрагмент onAttach
onCreate
onCreateView
onActivityCreated
onStart
onResume

Дополнительное чтение

xxv/android-lifecycle — схема жизненного цикла в SVG, PDF, PNG.

Источник

The Android Lifecycle cheat sheet — part III : Fragments

The diagrams are also available as a cheat sheet in PDF format for quick reference.

In this section we’ll cover the behavior of a fragment that is attached to an activity. Don’t confuse this scenario with that of a fragment added to the back stack (see Tasks and Back Stack for more information on fragment transactions and the back stack).

Scenario 1: Activity with Fragment starts and finishes

Note that it’s guaranteed that the Activity’s onCreate is executed before the Fragment’s. However, callbacks shown side by side — such as onStart and onResume — are executed in parallel and can therefore be called in either order. For example, the system might execute the Activity’s onStart method before the Fragment’s onStart method, but then execute the Fragment’s onResume method before the Activity’s onResume method.

Be careful to manage the timing of the respective execution sequences so that you avoid race conditions.

Scenario 2: Activity with Fragment is rotated

State management

Fragment state is saved and restored in very similar fashion to activity state. The difference is that there’s no onRestoreInstanceState in fragments, but the Bundle is available in the fragment’s onCreate , onCreateView and onActivityCreated .

Fragments can be retained, which means that the same instance is used on configuration change. As the next scenario shows, this changes the diagram slightly.

Fragments — Scenario 3: Activity with retained Fragment is rotated

The fragment is not destroyed nor created after the rotation because the same fragment instance is used after the activity is recreated. The state bundle is still available in onActivityCreated .

Using retained fragments is not recommended unless they are used to store data across configuration changes (in a non-UI fragment). This is what the ViewModel class from the Architecture Components library uses internally, but with a simpler API.

If you find errors or you think something important is missing, please report them in the comments. Also, let us know what other scenarios you would like us to write about.

Источник

Android, жизненый цикл Jetpack компонентов

Руководство по работе с жизненным циклом Android компонентов, рассмотрим базовые понятия, что такое LifecycleObserver, события и состояния жизненного цикла, кастомные LifecycleOwner.

используется: Kotlin 1.4, Android 10.0, Android Studio 4.2.

Android Jetpack — коллекция библиотек для разработчиков, которая улучшает код, уменьшает повторяющийся код и работает единообразно в различных версиях Android. Архитектурные компоненты Android Jetpack дают инструменты для создания компонентов в приложении с учетом жизненного цикла Activity или Fragment.

Читайте также:  Инженерное меню андроид сони иксперия

В этой статье, вы создадите компонент, учитывающий жизненный цикл (lifecycle-aware компонент), в приложении AwarenessFood. Компонент будет обрабатывать изменения сетевого подключения. Вы также создадите lifecycle owner для обновления состояние сети в Activity.

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

В статье вы изучите:

Жизненные циклы в Android компонентах

Компоненты учитывающие жизненный цикл (Lifecycle-aware components)

Наблюдателей жизненного цикла (Lifecycle observers)

События и состояния

Владельцев жизненного цикла (Lifecycle owners)

Как тестировать Lifecycle-aware компоненты

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

Начало

Загрузите материал с оригинальной статьи (кнопка Download вверху страницы здесь). Откройте Android Studio версии 4.2.1 или выше и импортируйте проект.

Ниже общая информация, что делает каждый пакет в проекте:

analytics классы для трекинга событий в приложении

data классы модели

di поддержка зависимостей

monitor содержит единственный класс, который смотрит за состоянием подключения к сети

network классы для доступа к внешним API

repositories классы для доступа к постоянному хранилищу

viewmodels бизнес логика

view экраны

Регистрация в spoonacular API

AwarenessFood приложение получает рецепты через spoonacular API. Вам нужно зарегистрироваться там, чтобы приложение работало правильно.

Заходим на сайт spoonacular.com и создаем новый аккаунт. После подтверждения аккаунта, входим в личный кабинет и ищем там API ключ. Копируем его, открываем файл RecipesModule.kt внутри пакета di и заменяем ключ в следующей строке:

Компилируем и запускаем. Должен появиться экран со случайным рецептом, похожий на такой:

Жмем на кнопку Reload, чтобы загрузить другой случайный рецепт. Если отключить интернет на устройстве и попробовать получить новый рецепт, то появится SnackBar с ошибкой и кнопкой повторить, как на изображении ниже:

Чтобы перейти на экран деталей, жмем в меню More пункт Food Trivia. Вы добавите этот функционал позже. Сейчас это просто экран с кнопкой:

На этом настройка проекта закончена. Сейчас вы готовы познакомиться с компонентами, учитывающие жизненный цикл (lifecycle-aware).

Жизненные циклы в Android

Важная базовая вещь для Android разработчика — это понимание, как работает жизненный цикл Activity и Fragment. Жизненный цикл представляет собой серию вызовов в определенном порядке при изменении состояния Activity или Fragment.

Жизненный цикл – необходимая концепция, потому что нужно понимать, что делать в определенных состояниях Activity и Fragment. Например, настройка layout (макета) происходит в Activity.onCreate() . Во Fragment за настройку layout отвечает метод Fragment.onCreateView() . Другой пример — включение чтения геолокации в методе onStart() .

Для освобождения ресурсов, чтение геолокации должно быть отключено в onStop() , здесь же освобождаются и другие ресуры и компоненты. Важный момент: не все lifecycle вызовы обязательно выполняются каждый раз. К примеру, операционная система может вызывать, а может и нет метод onDestory() .

Жизненный цикл Activity:

Для тех, кто хочет узнать побольше о жизненном цикле Activity, взгляните на статью Introduction to Android Activities With Kotlin.

Жизненный цикл Fragment:

Реагируем на изменения жизненного цикла

В большинстве приложений есть несколько компонентов, которым надо реагировать на события жизненного цикла Activity или Fragment. Вам нужно инициализировать или регистрировать их в методе onStart() и освобождать ресурсы в методе onStop() .

При таком подходе, ваш код может стать запутанным и подверженным ошибкам. Код внутри методов onStart() и onStop() будет нарастать бесконечно. При этом, легко забыть отписаться от компонента или вызвать метод компонента в неподходящем событии жизненного цикла, что может привести к багам, утечкам памяти и падениям приложения.

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

Примечание: Подробная информация о мониторинге сетевого соединения находится здесь — Read Network State.

Экземпляр NetworkMonitor должен быть доступен с момента инициализации Activity. Откройте MainActivity.kt и метод onCreate() , где монитор сети инициализируется вызовом networkMonitor.init() .

Затем NetworkMonitor регистрирует колбек в методе onStart() через вызов networkMonitor.registerNetworkCallback() . Наконец, экземпляр Activity отписывается от колбека в методе onStop , вызывая networkMonitor.unregisterNetworkCallback() .

Инициализация компонента, подписка на события через колбек и отписка от событий добавляет большое количество шаблонного кода в Activity. Кроме того, необходимо использовать только onStart и onStop для вызова методов NetworkMonitor .

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

Используем компоненты жизненного цикла (Lifecycle-Aware)

NetworkMonitor выполняет различные действия, зависящие от состояния жизненного цикла Activity. Другими словами, NetworkMonitor должен быть компонентом учитывающим жизненный цикл (lifecycle-aware компонент) и реагировать на изменения в жизненном цикле своего родителя – в нашем случае это MainActivity .

Android Jetpack предоставляет классы и интерфейсы для создания lifecycle-aware компонентов. Используя их, вы можете улучшить работу NetworkMonitor . Он будет работать автоматически с учетом текущего состояния жизненного цикла родительской Activity.

Владелец жизненного цикла (lifecycle owner) – это компонент имеющий жизненный цикл, такой как Activity или Fragment. Владелец жизненного цикла должен знать все компоненты, которые будут слушать события жизненного цикла. Паттерн «Наблюдатель» считается лучшим подходом для решения такой задачи.

Создание наблюдателя жизненного цикла (lifecycle observer)

Наблюдатель жизненного цикла компонент, который способен слушать и реагировать на состояния жизненного цикла своего родителя. У Jetpack есть специальный интерфейс для этого – LifecycleObserver .

Ну что, настало время улучшить наш NetworkMonitor . Откройте NetworkMonitor.kt и добавьте к классу поддержку интерфейса LifecycleObserver :

Вот и все, теперь это lifecycle наблюдатель. Вы только что сделали первый шаг по превращению NetworkMonitor в lifecycle-aware компонент.

События и состояния жизненного цикла

Класс Lifecycle знает состояние (state) жизненного цикла родителя и передает его любому прослушивающему LifecycleObserver .

Lifecycle использует два перечисления для обмена данными о жизненном цикле: Event и State.

Events

Event представляет события жизненного цикла, которые отправляет операционная система:

Каждое значение это эквивалент колбека жизненного цикла. ON_ANY отличается тем, что отправляется при каждом событии (можно сделать один метод для обработки всех событий).

Читайте также:  Точка вверху экрана андроид самсунг

Реакция на события жизненного цикла

Сейчас NetworkMonitor – это LifecycleObserver, но он пока не реагирует на жизненный цикл. Нужно добавить аннотацию @OnLifecycleEvent к методу, чтобы он начал реагировать на конкретное событие.

Добавьте @OnLifecycleEvent к нужным методам, как указано ниже:

В этом случае, метод init() реагирует на ON_CREATE событие, registerNetworkCallback() на ON_START и на событие ON_STOP вызывается unregisterNetworkCallback() .

Итак, NetworkMonitor теперь получает изменения жизненного цикла, сейчас нужно немного подчистить код. Следующий код больше не нужен в MainActivity.kt, потому что эти действия NetworkMonitor выполняет сам:

Полностью удаляйте onStart() и onStop() . Кроме того надо удалить networkMonitor.init() из метода onCreate() .

Сделав эти изменения в коде, вы переместили из Activity ответственность за инициализацию, регистрацию и освобождение ресурсов в сам компонент.

Состояния (States)

State хранит текущее состояние владельца жизненного цикла. Возможные значения:

Состояния жизненного цикла сигнализируют, что конкретное событие случилось.

В качестве примера, представим, что мы запускаем длительную инициализацию компонента в событии ON_START и Activity (или Fragment) уничтожаются до того, как инициализация закончится. В этом случае компонент не должен выполнять никаких действий по событию ON_STOP , так как он не был инициализирован.

Есть прямая связь между событиями и состояниями жизненного цикла. На диаграмме ниже, показана это взаимосвязь:

Эти состояния возникают при:

INITIALIZED: Когда Activity или Fragment уже созданы, но onCreate() еще не вызван. Это первоначальное состояние жизненного цикла.

CREATED: После ON_CREATE и после ON_STOP .

STARTED: После ON_START and После ON_PAUSE .

RESUMED: только после ON_RESUME .

DESTROYED: после ON_DESTROY , но прямо перед вызовом onDestroy() . Как только состояние становится DESTROYED , Activity или Fragment больше не будут посылать события.

Используем состояния (State) жизненного цикла

Иногда компоненты должны выполнять код, если их родитель находится, по крайней мере, в определенном состоянии. Нужно быть уверенным, что NetworkMonitor выполняет registerNetworkCallback() в нужный момент жизненного цикла.

Добавьте Lifecycle аргумент в конструктор NetworkMonitor :

С ним у NetworkMonitor будет доступ к состоянию жизненного цикла родителя.

После этого добавьте в код registerNetworkCallback() условие:

С этим условием NetworkMonitor будет запускать мониторинг состояния сети только, если состояние жизненного цикла не менее, чем STARTED . Это достаточно удобно, потому что жизненный цикл может измениться до завершения кода в компоненте. Зная состояние родителя можно избежать крешей, утечек памяти и гонок состояния в компоненте.

Итак, откройте MainActivity.kt и сделайте изменения для NetworkMonitor :

У NetworkMonitor сейчас есть доступ к родительскому жизненному циклу и прослушивание сети запускается только когда Activity находится в нужном состоянии.

Подписка на события жизненного цикла

Чтобы NetworkMonitor действительно начал реагировать на изменения, его родитель должен зарегистрировать NetworkMonitor как слушателя событий. В MainActivity.kt добавьте следующую линию в методе onCreate() после инициализации компонента:

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

Это отличное улучшение. С помощью одной строчки кода компонент будет получать события жизненного цикла от родителя. Больше не надо писать шаблонный код в Activity или Fragment. Кроме того, компонент содержит весь код инициализации и конфигурации, что делает его самодостаточном и тестируемым.

Соберите и запустите приложение снова. После загрузки рецептов включите «Самолетный режим». Вы увидите ошибку сети в SnackBar, так же как и раньше:

Однако внутреннняя реализация была значительно улучшена.

Кто владеет жизненным циклом?

Все выглядит хорошо, но. кто владеет жизненным циклом? Почему Activity сама владеет жизненным циклом в этом примере? Существуют ли другие владельцы?

Владелец жизненного цикла (lifecycle owner) – компонент реализующий интерфейс LifecycleOwner . В нем есть единственный метод, который необходимо реализовать: Lifecycle.getLifecycle() . Выходит, что любой класс поддерживающий этот интерфейс может быть владельцем жизненного цикла.

Android имеет встроенные компоненты с поддержкой жизненного цикла. Для Activity это ComponentActivity (является базовым классом для AppCompatActivity и реализует интерфейс LifecycleOwner ).

Однако, есть и другие классы с поддержкой LifecycleOwner . Например, Fragment это LifecycleOwner . Это значит, что можно переместить код в любой Fragment , если это надо и это будет работать точно так же, как и в MainActivity .

Жизненный цикл Fragment может быть значительно длиннее, чем цикл визуальных компонентов (UI), которых он содержит. Если наблюдатель взаимодействует с UI во Fragment, это может привести к проблемам, поскольку наблюдатель может изменить UI до инициализации или после уничтожения.

Это причина почему есть viewLifecycleOwner в Fragment . Вы можете использовать viewLifecycleOwner после вызова onCreateView() и перед onDestroyView() . Как только жизненный цикл будет уничтожен, он больше не будет отправлять события.

Распространенный случай использования viewLifecycleOwner это работа с LiveData в Fragment. Откройте FoodTriviaFragment.kt и добавьте в методе onViewCreated() , до viewModel.getRandomFoodTrivia() , следующий код:

Также надо будет добавить импорт класса import androidx.lifecycle.Observer .

Используя этот код, FoodTriviaFragment будет реагировать на события от foodTriviaState (является LiveData ). Так как Fragment является владельцем жизненного цикла ( viewLifecycleOwner ), наблюдатель будет получать обновления данных только, когда Fragment находится в активном состоянии.

Время сделать сборку и запустить приложение. Нажмите More в меню и выберите Food Trivia. Теперь можно получить несколько забавных и интересных Food Trivia в вашем приложении.

Использование ProcessLifecycleOwner

В некоторых случаях компоненту надо реагировать на изменения жизненного цикла самого приложения. К примеру, отследить когда приложение уходит в фоновый режим и возвращается на передний план. Для таких ситуаций в Jetpack есть ProcessLifecycleOwner, который естественно реализует интерфейс LifecycleOwner .

Этот класс представляет жизненный цикл всего процесса приложения. Событие ON_CREATE посылается только один раз, когда приложение стартует. При этом событие ON_DESTROY не будет посылаться вообще.

ProcessLifecycleOwner отправляет события ON_START и ON_RESUME , когда первая Activity проходит через эти состояния. Наконец, ProcessLifecycleOwner отправляет события ON_PAUSE и ON_STOP после того, последняя видимая Activity приложения проходит через соответствующие состояния.

Важно знать, что эти два последних события произойдут после определенной задержки. ProcessLifecycleOwner должен быть уверен в причине этих изменений. Он отправляет события ON_PAUSE и ON_STOP только если приложение перешло в фоновый режим, а не из-за изменения конфигурации.

При использовании такого владельца жизненного цикла, компонент должен также поддерживать интерфейс LifecycleObserver . Откройте AppGlobalEvents.kt в пакете analytics. Видно, что это LifecycleObserver .

Класс отслеживает, когда приложение выходит на передний план или переходит в фоновый режим. Это происходит когда владелец жизненного цикла посылает события ON_START и ON_STOP .

Читайте также:  Как антивирус самый лучший для андроид

Регистрация такого LifecycleObserver происходит немного по-другому. Откройте RecipesApplication.kt и добавьте следующий код в метод onCreate() :

Здесь мы получаем экземпляр ProcessLifecycleOwner и добавляем appGlobalEvents как слушателя событий.

Соберите и запустите приложение. После старта приложения, сверните его в фон. Если открыть LogCat и отфильтровать вывод по тегу APP_LOGGER, то вы должны увидеть сообщения:

Итак, вы увидели как Fragment, Activity и Application компоненты реализуют интерфейс LifecycleOwner . Но это еще не все. Сейчас вы узнаете, как создавать собственные классы, которые делают то же самое.

Создаем собственного владельца жизненного цикла

Как вы помните, любой класс может поддерживать интерфейс LifecycleOwner . Это значит, что можно создать кастомного владельца жизненного цикла.

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

Откройте UnavailableConnectionLifecycleOwner.kt в пакете monitor и сделайте изменения, чтобы класс поддерживал интерфейс LifecycleOwner :

После этого, добавьте LifecycleRegistry в UnavailableConnectionLifecycleOwner:

Класс LifecycleRegistry это реализация Lifecycle , который может работать с несколькими наблюдателями и уведомлять их о любых изменениях в жизненном цикле.

Добавляем события

Для уведомления о событиях жизненного цикла можно использовать метод handleLifecycleEvent() . Давайте добавим пару методов в наш класс:

В случае, когда смартфон потеряет сетевое соединение, lifecycleRegistry пошлет событие ON_START всем наблюдателям. Когда соединение снова появится, lifecycleRegistry отправит ON_STOP .

Наконец, добавьте следующий код:

addObserver() регистрирует слушателя жизненного цикла UnavailableConnectionLifecycleOwner .

Реакция на события

Теперь откройте MainActivity.kt и добавьте такую строчку кода:

После этого networkObserver будет реагировать на события unavailableConnectionLifecycleOwner . NetworkObserver покажет SnackBar, когда устройство потеряет сеть и скроет SnackBar при восстановлении сети.

И наконец, замените handleNetworkState() :

Этот код запускает события unavailableConnectionLifecycleOwner .

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

В следующей секции, вы узнаете как тестировать компоненты жизненного цикла.

Тестирование компонентов жизненного цикла

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

Эти тесты будут проверять, что вызывается нужный метод в NetworkMonitor в соответствии с состоянием владельца жизненного цикла. Чтобы создать тест, нужно пройти те же шаги, что и при создании кастомного владельца жизненного цикла.

Настройка тестов

Откройте NetworkMonitorTest.kt. Для запуска теста необходимо сделать заглушки (mock) для владельца жизненного цикла и NetworkMonitor . Добавьте две заглушки в тестовый класс:

Здесь используется функционал библиотеки MockK<:target="_blank">, она позволяет имитировать реализацию класса. Аргумент relaxed говорит, что заглушки могут работать без указания их поведения.

После этого создайте переменную:

Этот код добавляет объект LifecycleRegistry в тест, для управления наблюдателями и рассылки им событий жизненного цикла.

И наконец, добавьте следующую строчку кода в метод setup() :

Здесь инициализируется реестр LifecycleRegistry , в него передается заглушка владельца жизненного цикла и добавляется наблюдатель NetworkMonitor , чтобы слушать события.

Добавляем тесты

Чтобы убедиться, что нужный метод вызывается в NetworkMonitor , реестр жизненного цикла должен установить правильное состояние и уведомить своих слушателей. Для этого будет использоваться метод handleLifecycleEvent() .

Первый тест будет проверять, что при событии ON_CREATE вызывается метод init() .

Давайте напишем тест:

Сначала устанавливаете состояние ON_CREATE

После этого, проверяете, что был вызван метод init() в NetworkMonitor .

Не забудьте импортировать зависимости:

Запускайте тест, он проходит успешно.

Теперь давайте проверим событие ON_START , оно должно вызывать метод registerNetworkCallback() :

Этот код проверяет ON_START событие.

Тест должен пройти успешно.

В конце концов, создадим тест для проверки события ON_STOP и вызова метода unregisterNetworkCallback() :

Этот код проверяет ON_STOP событие.

Запустите тест и . он падает с ошибкой Verification failed: call 1 of 1: NetworkMonitor(#2).unregisterNetworkCallback()) was not called. Это значит, что unregisterNetworkCallback() не выполнился при событии ON_STOP ? Тестовый код посылает событие ON_STOP , но перед ним обязательно должно быть событие ON_START .

Добавляем событие ON_START в тест:

Теперь тест проходит успешно.

Таким образом, мы проверили все события жизненного цикла и они вызывают нужные методы в NetworkMonitor .

LiveData: компонент жизненного цикла

К этому моменту вы узнали, как создавать собственный компонент жизненного цикла. Но существуют ли такие же готовые компоненты в Android? Конечно, возможно самый известный из них это LiveData.

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

Наблюдатель в активном состоянии если его жизненный цикл в состоянии STARTED или RESUMED . Если жизненный цикл в другом состоянии, LiveData не будет уведомлять слушателей об изменениях.

Создание и присваивание переменных LiveData

Откройте MainViewModel.kt из пакета viewmodels. Добавьте там следующие переменные:

_loadingState это LiveData с двумя возможными значениями: Loading и NotLoading . Эти значения будут говорить UI показывать или скрывать загрузку ( ProgressBar ).

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

Давайте изменим метод getRandomRecipe() :

Теперь обновление value будет отправлено всем слушателям _loadingState .

Наблюдения за изменениями LiveData

Откройте MainActivity.kt и добавьте код в метод onCreate() :

Здесь, MainActivity начнет наблюдать за обновлениями от viewModel.loadingState . Как вы видите, первый аргумент observe() это this , то есть текущий экземпляр MainActivity . Взгляните на сигнатуру метода observe() , там видно, что первый аргумент имеет тип LifecycleOwner . Это значит, что наблюдатели LiveData будут реагировать на изменения в зависимости от состояния жизненного цикла Activity. Для того чтобы открыть сигнатуру метода, нажмите Control— или Command-click.

В методе observe() есть такой код:

Видно, если LifecycleOwner в состоянии DESTROYED и новое значение было установлено для переменной, то наблюдатель не получит обновление.

Однако, если LifecycleOwner становится активным опять, то наблюдатели получат последнее значение автоматически.

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

Поздравляю! Вы отрефакторили проект с использованием компонентов жизненного цикла.

Что посмотреть?

Официальная документация по архитектурным компонентам: Architecture Components: Official Documentation.

Дополнительные материалы по тестированию Jetpack компонентов: Testing Android Architecture Components.

Источник

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