Если взглянуть на исходный код паттерна 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 и настройте его следующим образом. Это вполне очевидная последовательность действий. Мы подключаем все созданные на предыдущих шагах компоненты.

Если проект запустится успешно, вы увидите подобный результат.

Читайте также:

Читайте нас в TelegramVK и Дзен


Перевод статьи Michael M.H: Basic Android ViewModel in 5 minutes

Предыдущая статьяФайл package-lock.json: полное руководство
Следующая статьяКак написать тест-раннер в 80 строк кода на JavaScript/TypeScript