Если взглянуть на исходный код паттерна MVVM (Model-View-ViewModel), то для многих он представляется смесью из различных библиотек Jetpack, таких как ViewModel, LiveData, Data Binding, Room, Dagger 2 и т. д. Это может сбивать с толку, в особенности начинающих. Попробуем разобраться.
Что такое ViewModel
Проще говоря, ViewModel является одним из компонентов, включенных в Android Jetpack. Объект ViewModel содержит бизнес-логику обработки данных для взаимодействия с моделью, а также предоставляет данные для определенного компонента пользовательского интерфейса (UI), например Fragment или Activity. Другими словами, это просто интерфейс между UI и данными.
Например, когда экран поворачивается, в Activity и Fragment он создается заново. Поэтому временные данные, управляемые этим событием, будут потеряны. Однако использование ViewModel позволяет наследовать (сохранять) данные даже в этом случае.
Кроме того, это соответствует фрагменту VM при разработке с архитектурой MVVM. Связь между View (Activity, Fragment и т. д.) и ViewModel выглядит следующим образом.
Жизненный цикл ViewModel
ViewModel имеет более длительный жизненный цикл, чем целевые интерфейсы LifecycleOwners. Они реализуются классом, который владеет каким-либо элементом или сохраняет его, запуская в течение определенного жизненного цикла, например Activities и Fragments.
Даже если Activity и Fragment будут воссозданы при повороте экрана, ViewModel все равно сможет работать и сохранить данные.
Жизненный цикл ViewModel:
Приложение с использованием ViewModel
Создадим простое приложение с использованием ViewModel. Оно будет складывать два числа (значения a и b), а затем выводить результат на экран при нажатии кнопки CALCULATE.
Сначала откройте файл build.gradle(app)
и добавьте в блок зависимостей следующую реализацию:
def lifecycle_version = "2.3.1" // ViewModel implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" // LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
Добавьте класс Kotlin в папку java. Я назвал ее CalculationViewModel
. Все что нужно сделать — это расширить класс ViewModel()
.
class CalculationViewModel(private val calculations: Calculations) : ViewModel() {
// Здесь напишите логику
}
Я создал отдельный интерфейс под названием Calculations
, который установил в качестве аргумента в CalculationViewModel
. Этот интерфейс включает функцию под названием calculateAddition
, которая будет выполнять сложение. Мы будем использовать ее в CalculationViewModel
. Это мой способ решения, и он не связан с ViewModel.
interface Calculations {
fun calculateAddition(a: Int, b: Int): Int
}
Создание класса ViewModelFactory
, генерирующего ViewModel
, — это вовсе не обязательная, но рекомендуемая практика. Создадим класс, который расширяет ViewModelProvider.Factory
, и назовем его CalculationViewModelFactory
. Мы будем использовать этот класс позже в MainActivity
при передаче этого класса в ViewModelProvider
.
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider class CalculationViewModelFactory( private val calculations: Calculations ) : ViewModelProvider.Factory { override fun <T : ViewModel?> create(modelClass: Class<T>): T { return CalculationViewModel(calculations) as T } }
Далее создайте класс (я назвал его MyCalculation
), который унаследует интерфейс Calculation
, созданный на шаге 2. Затем мы переопределим функцию calculate Addition()
и в ней напишем простую логику сложения, которая возвращает результат.
class MyCalculation : Calculations {
override fun calculateAddition(a: Int, b: Int): Int {
return a + b
}
}
Затем вернемся к CalculationViewModel
и начнем добавлять логику и необходимые нам компоненты (такие как MutableLiveData
для привязки, функции для проверки равенству нулю значений a и b, вызов calculateAddition()
и т. д.).
Этот код беспорядочен, так как он написан для тестирования. Вы можете попытаться его улучшить.
После настройки ViewModel
создадим макет при помощи Databinding. Ознакомьтесь с привязкой данных (Databinding), если это новая для вас тема.
Displays variables android:text=”@={myViewModel.a}” Executes a function android:onClick=”@{()->myViewModel.calculateAddition()}”
Мы привяжем переменную из ViewModel.
В фрагменте ниже обратите внимание на код, выделенный жирным шрифтом.
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <data> <variable name="myViewModel" type="com.yourpackagename.CalculationViewModel" /> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="15dp" android:orientation="vertical" tools:context=".MainActivity" > <EditText android:id="@+id/a_value" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="5dp" android:layout_marginBottom="5dp" android:ems="10" android:text="@={myViewModel.a}" android:hint="a value" android:inputType="number" android:layout_gravity="center_horizontal" android:textSize="20sp" android:textStyle="bold" /> <EditText android:id="@+id/b_value" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="5dp" android:layout_marginBottom="5dp" android:ems="10" android:text="@={myViewModel.b}" android:hint="b value" android:inputType="number" android:layout_gravity="center_horizontal" android:textSize="20sp" android:textStyle="bold" /> <Button android:id="@+id/calculate_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="@string/calculate" android:layout_marginBottom="15dp" android:onClick="@{()->myViewModel.calculateAddition()}" android:textSize="20sp" android:textStyle="bold" /> <TextView android:id="@+id/addition_textview" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="5dp" android:layout_marginBottom="5dp" android:ems="10" android:text="@={myViewModel.additionResult}" android:gravity="center_horizontal" android:textSize="20sp" android:textStyle="bold" /> </LinearLayout> </layout>
Наконец, перейдите к MainActivity и настройте его следующим образом. Это вполне очевидная последовательность действий. Мы подключаем все созданные на предыдущих шагах компоненты.
Если проект запустится успешно, вы увидите подобный результат.
Читайте также:
- Вся правда об использовании навигационной библиотеки Jetpack в модульных проектах
- Быстрое перенаправление в приложение с AutoVerify
- Повторное использование UI в Android - 5 главных ошибок
Читайте нас в Telegram, VK и Дзен
Перевод статьи Michael M.H: Basic Android ViewModel in 5 minutes