Делегируйте события своего жизненного цикла в ViewModel

ViewModels существуют, чтобы убрать всю бизнес-логику из представления. Независимо от того, как вы разрабатываете свою ViewModel, некоторая логика остается тесно связанной с жизненным циклом View. Например, небольшим вариантом использования может быть выполнение некоторого отслеживания каждый раз, когда вы открываете экран. Предполагая, что этот экран внедряет ViewModel, и эта ViewModel по праву владеет логикой отслеживания, вам нужно будет определить, когда запускать такое действие.

Поскольку жизненный цикл принадлежит сфере представления, вы можете оставить это на ответственности представления — например, переопределив функцию onResume.

Но давайте не будем забывать, почему мы используем ViewModels. В конце концов, мы собираемся извлечь эту бизнес-логику в ViewModel, чтобы протестировать ее. С этой архитектурой мы могли бы протестировать реализацию — трек был вызван с правильными аргументами, вызывая verify, если вы используете Mockito.

К сожалению, мы не можем проверить наше поведение. Мы не можем утверждать, что отслеживание происходило, когда пользователь открывал экран. Только то, что отслеживание правильно реализовано. Это огромная разница! И не говоря уже о том, что он не будет хорошо масштабироваться. Если случайно кто-то удалит эту часть кода в вашем представлении, вы получите:

  • Регрессия, поскольку вы больше не будете отслеживать свой открытый экран.
  • Какой-то мёртвый код в вашей ViewModel.
  • Зеленые модульные тесты, охватывающие вариант использования без подключения к сети.

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

Компоненты с учетом жизненного цикла

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

ViewModel уже отвечает на события жизненного цикла с помощью обратного вызова onCleared. Жизненный цикл ViewModel отличается от View, но получает уведомление, когда он больше не нужен.

Чтобы еще больше повысить осведомленность о его жизненном цикле, мы можем положиться на библиотеку Jetpack от Google androidx.lifecycle. Если вы хотите, чтобы компонент учитывал жизненный цикл, просто реализуйте его интерфейс LifecycleObserver и объявите его наблюдателем от владельца жизненного цикла, за которым вы хотите наблюдать. Если вам не нужно реагировать на все события жизненного цикла, вы можете использовать интерфейс DefaultLifecycleObserver.

Благодаря этой ViewModel с учетом жизненного цикла вам больше не нужно переопределять onResume в своем представлении. ViewModel заботится обо всей бизнес-логике. Поздравляем!

Но как насчет тестов? Вот почему мы делаем это правильно?

Тестирование, тестирование

Я написал пустую оболочку для тестирования нашей ViewModel.

Чтобы этот тест работал, нам нужен механизм перехода из одного состояния в другое. Нам понадобятся три вещи:

  1. LifecycleRegistry для управления состоянием жизненного цикла.
  2. LifecycleOwner переходим к LifecycleRegistry.
  3. A LifecycleObserver: проверенная ViewModel для нашего варианта использования.

Чтобы создать LifecycleRegistry, мы можем использовать метод createUnsafe, специально разработанный для тестов. Мы введем LifecycleOwner в LifecycleRegistry в качестве макета:

val lifecycleOwner = mock<LifecycleOwner>()
val lifecycleRegistry = LifecycleRegistry.createUnsafe(lifecycleOwner)

Затем нам нужно зарегистрировать ViewModel в файле LifecycleRegistry.

lifecycleRegistry.addObserver(viewModel)

Наконец, вы можете указать желаемое состояние жизненного цикла:

lifecycleRegistry.currentState = Lifecycle.State.RESUMED

Вот наш проверенный класс:

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

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

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

Заключительные мысли

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

Делая ViewModel осведомленным о жизненном цикле, вы делегируете некоторую логику, принадлежащую View, ViewModel. Ваше представление может только обновлять свое состояние и выполнять эффекты, исходящие от ViewModel. Больше ничего!

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

Всегда сводите код просмотра к минимуму.