Афоризм
Я в браке третий раз. Опять попался брак.
Наталья Резник
Последние статьи

 • Активности Android
Многоэкранные Android приложения
 • Fragment dynamic
Динамическая загрузка фрагментов в Android
 • Fragment lifecycle
Жизненный цикл Fragment'ов в Android
 • Fragment example
Пример Fragment'ов в Android
 • Data Binding
Описание и пример Data Binding
 • Пример MVVM
Пример использования MVVM в Android
 • Компонент TreeTable
Описание компонента TreeTable для Swing
 • Пример TreeTable
Пример использования TreeTable
 • Хранилища Android
Внутренние и внешние хранилища данных
 • Пример SQLite
Пример использования SQLite в Android
 • WebSocket
Описание и пример реализации WebSocket
 • Визуальные компоненты
Улучшен компонент выбора даты из календаря
 • Анимация jQuery
Описание и примеры анимации элементов DOM
 • APK-файл Android
Создание apk-файла для android устройств, .dex файлы
 • платформа JaBricks
Платформа OSGi-приложения JaBricks
Поддержка проекта

Если Вам сайт понравился и помог, то будем признательны за Ваш «посильный» вклад в его поддержку и развитие
 • Yandex.Деньги
  410013796724260

 • Webmoney
  R335386147728
  Z369087728698

Model-View-ViewModel в Android

Заметка : данная статья является продолжением описания комплексного использования Data Binding и MVVM (Model-View-ViewModel).

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

Шаблон Model-View-ViewModel (MVVM) позволяет четко отделить интерфейс представления от бизнес-логики (java-кода). Поддержка разделения логики приложения и пользовательского интерфейса помогает исключить многочисленные проблемы разработки и упростить тестирование, обслуживание и развитие приложения. MVVM также может значительно улучшить возможности повторного использования кода и позволяет программистам и дизайнерам пользовательского интерфейса упростить групповую разработку соответствующих частей приложения.

MVVM — это шаблон определения архитектуры клиентских приложений, предложенный Джоном Госсманом (John Gossman) как альтернатива шаблонам MVC и MVP при использовании технологии связывания данных (Data Binding). Концепция MVVM связана с отделением логики представления данных от бизнес-логики путем вынесения её в отдельный класс для более четкого разграничения.

В шаблоне MVVM есть три основных компонента : модель (Model), представление (View) и модель представления (ViewModel). Каждый из компонентов выполняет отдельную функцию. На скриншоте показаны связи между тремя компонентами.

Рассмотрим каждый из компонентов шаблона Model-View-ViewModel :

КомпонентОписание
Model Связанная с данными приложения логика. Иными словами - это POJO, классы работы с API, базой данных и пр.
View Это интерфейс (layout) приложения, в котором располагаются все необходимые виджеты для отображения информации.
ViewModel Компонент шаблона, в котором описывается логика поведения View в зависимости от результата работы Model. Можно назвать его моделью поведения View. К примеру, это может быть как форматирование текста, так и логика управления видимостью компонентов или отображения состояний, таких как загрузка, ошибка, пустые экраны и т.д. Также в этом компоненте описывается поведение, которое было инициировано пользователем (ввод текста, нажатие на кнопку и т.п.)

Преимущества и недостатки

Какие преимущества несет технология использования Data Binding и MVVM :

  • Гибкость разработки. Данный подход повышает удобство работы в команде, поскольку разработка проекта идёт одновременно по всем направлениям — компоновка и стилизация интерфейса с одной стороны и бизнес-логика получения и обработки данных с другой;
  • Разграничение логики. За счет отделения интерфейсной части от логики код становится более гибким и простым в читабельности и поддержке, поскольку каждый элемент отвечает за конкретную функцию.
  • Тестирование. Упрощается процесс написание тестов и создания mock-объектов. Также, в большинстве случаев отпадает потребность в автоматизированном UI-тестировании, т.к. ViewModel можно обернуть unit-тестами;

Оборотная сторона медали, связанная с недостатками :

  • для небольших проектов использование Data Binding и MVVM подход может быть неоправданным;
  • в случае, если логика привязки данных слишком сложная, то и отлаживать приложение будет труднее.

Сценарий использования Data Binding и MVVM

Рассмотрим пример использования технологии Data Binding и MVVM. Допустим, у нас имеется простой объект типа User, класс которого имеет следующий вид :

Листинг User.java


import androidx.annotation.NonNull;
import androidx.databinding.Bindable;
import androidx.databinding.BaseObservable;

public class User extends BaseObservable
{
    private  int     age;
    private  String  name;

    //---------------------------------
    public User(int age, String name) {
        this.age = age;
        this.name = name;
    }
    //---------------------------------
    @Bindable
    public int getAge() {
        return age;
    }
    //---------------------------------
    public void setAge(int age) {
        this.age = age;
        notifyPropertyChanged(BR.age);
        notifyPropertyChanged(BR.adult);
    }
    //---------------------------------
    @Bindable
    @NotNull(message="Name cannot be empty")
    public String getName() {
        return name;
    }
    //---------------------------------
    public void setName(String name) {
        this.name = name;
        notifyPropertyChanged(BR.name);
    }
    @Bindable
    public boolean isAdult()
    {
        return age >= 18;
    }
}

Класс User имеет всего 2 поля (name, age) и методы обращения к ним Get/Set для определения и получения значений. Выделенные в листинге строки кода относятся к элементу шаблона ModelView для наблюдения за полями объекта. Для этого, класс User наследует свойства BaseObservable, а к методам Get (getName, getAge) добавляются аннотации @Bindable, согласно которым изменение значений этих полей/свойств будут контролироваться (наблюдаться). При этом, функция isAdult() также участвует в наблюдении, хотя физического представления поля adult объект не имеет. Аннотацию @Bindable можно использовать только в том случае, если класс наследует свойства BaseObservable.

При изменении значений свойств объекта вызываются методы Set (setName, setAge), в которых выполняется вызов метода notifyPropertyChanged, который уведомляет объект связи Binding об изменениях значения поля.

Аннотация @NotNull используется в классе для контроля за заполнением значения поля, которое не может быть пустым.

Observable поля

В листинге класса User используется наблюдение за всем объектом, поскольку User расширяет (extends) свойства BaseObservable. Но можно следить не за всем объектом, а за отдельными его свойствами. Для этого имеются готовые Observable поля: ObservableBoolean, ObservableInt, ObservableFloat и т.п. Кроме этого, можно использовать поле ObservableField с указанием типа. Ниже представлен класс User, в котором осуществляется наблюдение не за всем объектом, а за его полями age, name :

public class User
{
    public ObservableInt            age;
    public ObservableField<String> name;

    age  = new ObservableInt();
    name = new ObservableField<String>();

    //---------------------------------
    public User(int age, String name) {
        this.age .set( age);
        this.name.set(name);
    }
    //---------------------------------
}

Примечание : а вот здесь мы не можем включить в наблюдение метод isAdult(), поскольку функция зависит от age, а реализация методов скрыта. Если включить в класс поле adult типа ObservableBoolean, то необходимо будет определять его значение при изменении age.

Определение бизнес-логики в layout

Ниже представлен листинг макета (layout) activity_main.xml, используемый в примере. Особенности использования Data Binding и MVVM в макете :

  • секция <data> включает
    • переменную (variable) user с типом класса com.example.dbmvvm.User;
    • импорт android.view.View, необходимый для реализации опции android:visibility.
  • android:id определяют идентификаторы View компонентов.
<?xml version="1.0" encoding="utf-8"?>

<layout xmlns:tools="http://schemas.android.com/tools"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  xmlns:android="http://schemas.android.com/apk/res/android">

<data>
   <variable
        name="user"
        type="com.example.dbmvvm.User"/>
    <import type="android.view.View" />
</data>

<androidx.constraintlayout.widget.ConstraintLayout
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/userName"
        android:layout_width="160dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:text="@{user.name}"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/userAge"
        android:layout_width="160dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:text="@{@string/age_format(user.age)}"
        android:visibility="@{user.adult ? View.VISIBLE :
                                           View.INVISIBLE}"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf=
                                        "@+id/userName" />
    <Button
        android:id="@+id/button"
        android:layout_width="160dp"
        android:layout_height="48dp"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:text="@string/button"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="
                                          @+id/userAge" />
</androidx.constraintlayout.widget.ConstraintLayout>

</layout>
  Рейтинг@Mail.ru