Делегируйте события своего жизненного цикла в 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.
Чтобы этот тест работал, нам нужен механизм перехода из одного состояния в другое. Нам понадобятся три вещи:
LifecycleRegistry
для управления состоянием жизненного цикла.LifecycleOwner
переходим кLifecycleRegistry
.- 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, ничто не мешает вам учитывать жизненный цикл других компонентов. Это может пригодиться в некоторых случаях использования, когда компоненту владельца бизнеса потребуется доступ к этой информации, чтобы выполнить какое-либо действие, не подвергая их представлению.
Всегда сводите код просмотра к минимуму.