- Быстрый старт Data Binding в Android
- Введение
- Начало
- Настройка Layout
- Привязка данных
- Конвертеры
- Обратная связь и Binding
- ObservableFields
- Из View в Model
- Реализация паттерна MVVM на Android через Data Binding
- Проблема
- Решение
- Схема паттерна MVVM
- Пример
- Выводы
- Подпишитесь
- 5 января 2017 г. Реализовываем MVVM в Android
- Что такое MVVM?
- И все же — кто есть кто?
- Делать — так наверняка
- Несколько полезных примеров
- Забудем о boilerplate!
- Резюмируя
Быстрый старт Data Binding в Android
Введение
Профессионально андроид-разработкой занимаюсь чуть больше года, до этого разрабатывал по Windows Phone и мне понравилась возможность связывать данные из вью модели с самим View при помощи механизма Bindings. А после изучения RX, многие задачи стали решаться более чисто, вью-модель полностью отделилась от View. Она стала оперировать только моделью, совсем не заботясь о том, как она будет отображаться.
В Android такой строгости я не заметил, Activity или Fragment как простейшие представители контроллера чаще всего имеют полный доступ как ко View, так и к модели, зачастуя решая, какой View будет видим, решая таким образом чисто вьюшные задачи. Поэтому я довольно радостно воспринял новость о появлении Data Binding в Android на прошедшем Google IO.
Пока что это только бета релиз, но уже можно протестировать функционал и увидеть направление, в котором двигаются разработчики из Google.
Начало
Я использую Android Studio 1.3. Data binding поддерживает Android 2.1 и выше (API level 7+).
Для сборки используется новый android плагин для Gradle (нужна версия 1.3.0-beta1 и старше). Так как связи отрабатываются во время компиляции, нам понадобиться ещё один плагин к Gradle ‘com.android.databinding:dataBinder:1.0-rc0’. В отличие от того же Windows Phone где механизм привязок реализован глубоко по средством DependencyProperty и в RealTime, в Android эта функция реализуется как бы поверх обычных свойств, во время компиляции и дополнительной кодогенерации, поэтому в случае ошибок будьте готовы разбирать ответ от компилятора.
Итак, заходим в файл build.gradle, который лежит в корневом каталоге проекта (в нём идут настройки Gradle для всего проекта). В блоке dependencies вставляем:
Теперь подключим плагин к конкретному модулю, откроем build.gradle файл, который лежит внутри модуля. По умолчанию app/build.gradle и добавим строчку:
Настройка Layout
Мы должны обернуть наш внешний View в тег
Уже сейчас можно начать его использовать класс Binding для доступа к элементам интерфейса, без использования findViewById. В MainActivity добавим поле и перепишем метод onCreate:
Название поля берётся из Id View, без Id в биндере поле не появиться, если изменить Id View, то поле в биндере сразу же переметнуться. Если с зажатым CTRL нажать на название поля View, то сразу перейдешь к нему в файле разметки. Как по мне так уже одного такого функционала достаточно для того чтобы начать использовать биндинги.
Привязка данных
Например у нас есть карточка пользователя имя и возраст.
Изменим Layout, заменим содержимое LinearLayout на:
И в onCreate заменим последнюю строку на:
Запускаем. Всё работает.
Наверное у всех проектах в активити или в фрагментах встречается такие строчки:
Тут то мы и начинаем использовать непосредственно привязки данных. Перепишем модель:
И добавим в Layout:
На красные выделения студии игнорируем.
Так как мы используем класс View, то его нужно импортировать, добавим в ноду
Или используем его вместе с названием пакета:
Так же возможно в ноде
Конвертеры
Импорт в свою очередь даёт возможность писать конвертеры. Добавим в модель поле с датой рождения и удалим возраст:
Импортируем его в разметку:
Обратная связь и Binding
Попробуем сменить имя пользователя.
Добавим в Layout:
Запускаем и кликаем, тост всплыл, но имя не изменилось. Это случилось из-за того, что модель ни как не известила binder о своём изменении.
Можно создать новую модель и вставить её, но с точки зрения памяти это расточительно:
Или вытащить старую, заменить данные и вставить опять:
Но тогда обновятся все View, связанные с этой моделью. Лучшим вариантом будет связать модель с binder, чтобы она могла его оповестить о своём изменении. Для этого перепишем класс модели, добавив геттеры и сеттеры,
помечая геттеры атрибутом @Bindable, и добавив в сеттеры вызов notifyPropertyChanged(BR.lastName);
Видим новый класс BR, в котором содержатся идентификаторы полей, чьи геттеры помечены атрибутом @Bindable . В Layout оставляем android:text=»@
ObservableFields
В пакете android.databinding есть классы, которые могут упростить нотификацию binder об изменении модели:
- Обёртки над элементарными типами
- ObservableField
- ObservableArrayMap
- ObservableArrayList
Попробуем изменить модель:
По коллекциям аналогично, единственное приведу пример обращения ко ключу к Map:
Из View в Model
Теперь попробуем взять новое имя из UI, привязав EditText к некоторой модели. Так как внутри EditText крутится Editable, то и привязать будет к
Изменю MainActivity:
А в разметку добавлю:
Вот тут возникает проблема, если привязать ObservableField к EditText, то всё будет работать только в сторону View. Как я понял, проблема в том, что Editable, который лежит внутри ObservableField, отличается от того, который лежит внутри EditText.
Если у кого есть идеи — делитесь.
Очень любопытно было увидеть библиотеку для поддержки Data Binding в Android от Google. В документации тоже нет информации про обратную связь данных, но я надеюсь на скорую её реализацию. После официального выхода стабильной версии можно будет посмотреть на интеграцию с JavaRX.
[ Оффициальная документация ]
[ Ссылка на мой простенький пример ]
Источник
Реализация паттерна MVVM на Android через Data Binding
Время чтения: 12 минут
Отправим вам статью на:
При разработке сложных UI решений в Android приложениях зачастую приходится писать много шаблонного кода. Например, когда пользователь вводит или получает какие-то данные, некоторые View могут изменять свои параметры text, visibility, enable и т.д. При сложной логике код фрагмента или активити обрастает кучей сеттеров setEnabled(), setVisibility() setText() и т.д. Всё это приводит к увеличению кода, а, следовательно, и к росту числа багов.
К счастью, появилась библиотека databinding, которая позволяет решить эту проблему, сделать код более удобным, читаемым и избежать большого количества шаблонного кода. Она позволяет привязать к лейауту структуры данных, следить за их изменениями и в режиме реального времени отображать их в xml-разметке.
Конечно, это не первая библиотека, реализующая binding. Но Data Binding отличается от аналогов (Bindroid, RoboBinding, ngAndroid) по следующим параметрам:
- Является официальной библиотекой от Google, а, следовательно, не испытывает каких-либо проблем с AppCompat, RecyclerView и другими компонентами Android.
- Проверка ошибок осуществляется на этапе компиляции.
- Происходит генерация кода, а не работа с reflection.
Библиотека пока имеет свои недочёты, но они незначительны. Рассмотрим, каким образом можно реализовать паттерн MVVM на Android с использованием Data Binding.
Проблема
В стандартном подходе разработки под Android данные, логика и представление не разделены и находятся в коде фрагмента или активити. Традиционный подход будет неудобен при разработке приложений со сложной объёмной логикой по тем же причинам, что и при разработке сложных UI решений: много кода —> смешивание логики и представления —> баги.
Решение
Для решения этой проблемы мы можем реализовать паттерн Model View ViewModel (MVVM) через Data Binding, которая открывает возможности для разделения данных, логики и представления.
Схема паттерна MVVM
Ключевая идея заключается в том, чтобы через databinding привязать объект ViewModel к представлению (через лейаут), специфичные моменты взаимодействия с fragment/activity реализовать через interface (например, смена fragment/activity), а во ViewModel описать всю логику. Т.е. наша ViewModel выступает прослойкой между Model и View. Таким образом, мы получим гибкую распределённую систему, где каждый элемент играет свою роль и не мешает другому.
Рассмотрим это на примере экрана авторизации:
Пример
Как видно из этого экрана, кнопка Sign in становится enabled только когда оба текста EditText введены корректно (для email — проверка паттерна email, для password — количество символов > 3). Реализуем это с помощью паттерна MVVM.
Код ViewModel:
Во ViewModel мы создаём 2 метода, возвращающих два TextWatcher, и метод для обработки нажатия кнопки Done на клавиатуре. Для связи с SignInFragment в SignInViewModel описывается интерфейс SignInDataListener, реализация которого передаётся в конструкторе SignInViewModel.
Вся обработка и логика проверки на валидность введённых данных происходит в классе SignInRequest, который наследуется от BaseObservable. Также есть 2 метода для прослушки кликов по кнопкам Sign in и Sign up. Для кнопки Sign up мы просто вызываем метод реализованного интерфейса onSignUpClicked. Для кнопки Sign in сначала посылаем запрос, выполняем всю работу по его обработке, если всё удачно — вызываем метод реализованного интерфейса onSignInCompleted, если возникли проблемы — обрабатываем их.
Таким образом, вся логика и магия происходит во ViewModel.
Рассмотрим, как это привязывается к нашему лейауту:
В теге variable передаём нашу ViewModel с параметрами name=”viewModel”, type=”com.azoft.mvvm.SignInViewModel”. И для EditText’ов устанавливаем соответствующие TextWatcher и EditorAction. Для кнопок устанавливаем соответствующие обработчики нажатия, а для кнопки Sign in параметр enabled, который зависит от метода isInputDataValid класса SignInRequest. Этот метод всегда возвращает актуальные данные о валидности введённых полей, т.к. SignInRequest наследуется от BaseObservable и при установке его полей в сеттерах вызывается метод notifyChange(), уведомляющий о том, что данные изменились.
Связь View и ViewModel:
В автогенерируемом классе FragmentSignInBinding мы привязываем наш layout к SignInFragment и передаём туда SignInViewModel.
Fragment реализует интерфейс, определённый во ViewModel, в нашем случае это SignInDataListener. Тем самым обозначая, что должно произойти во View после того, как мы успешно прошли авторизацию или нажали на кнопку Sign up.
Таким образом, View (в нашем случае Fragment) ничего не знает о том, что именно происходит при нажатии кнопки или вводе данных. Ей только говорят, как она должна измениться или обновиться.
Конечно, нужно не забывать сохранять состояние ViewModel и привязывать к lifecycle SignInFragment’а. Это не описано в коде, чтобы не загромождать его.
Таким образом мы получаем распределённую систему, где разделены логика и представление. Эта система хороша тем, что большая часть логики ViewModel часто одна и та же в разных приложениях и может повторяться в рамках одного проекта. То есть, большая часть кода ViewModel может быть использована при создании любых приложений на Android даже с учётом того, что представления зачастую уникальны для каждого приложения.
Для ознакомления вы можете посмотреть другие сэпмплы приложений, доступные на GitHub: https://github.com/ivacf/archi. Здесь представлены наглядные примеры работы с экраном поиска и списка.
Выводы
Плюсы выбранного подхода:
- Очень удобен на сложных экранах со сложной логикой и UI.
- Возможность использования всех преимуществ Databinding library(ObservableFields, не нужно вызывать findViewById или подключать Butterknife или аналогичные бибилиотеки, Binding adapters и т.д.).
- Существенно упрощает написание тестов, т.к. логика отделена от представления.
Минусы:
- Необходимость сохранения состояния ViewModel.
- Не всегда можно разделить логику от представления.
В целом, от использования данного подхода остаются только положительные эмоции. Различные возникающие проблемы, такие, как сохранение состояния ViewModel и привязка к lifecycle fragment/activity, решаются несложно. Используя предложенный подход, вы сможете довольно легко создавать приложения с насыщенным интерфейсом и, в то же время, получать простые и компактные ViewModel.
- Поделиться в Facebook
- Share on Linkedin
- Запостить в Twitter
- Сохранить в Pocket
- 0 Репосты
—>
Подпишитесь
Оставьте адрес, и каждый месяц мы будем высылать свежую статью
о новых трендах в разработке програмного обеспечения.
Источник
5 января 2017 г. Реализовываем MVVM в Android
Пришло время научиться делать гибкую архитектуру для Android, используя DataBinding!
Привет всем! Прежде всего хотелось бы извиниться за 9 месяцев затишья с момента публикации статьи о DataBinding. Все никак не хватало времени написать обещанное продолжение. Но в этом есть и плюсы — за это время мы успели “обкатать” некоторые решения и сделать их еще лучше 😉
Что такое MVVM?
Для начала давайте рассмотрим классическое описание этого шаблона и разберем каждый из его компонентов. Итак, Model-View-ViewModel (т.е. MVVM) — это шаблон архитектуры клиентских приложений, который был предложен Джоном Госсманом (John Gossman) как альтернатива шаблонам MVC и MVP при использовании технологии связывания данных (Data Binding). Его концепция заключается в отделении логики представления данных от бизнес-логики путем вынесения её в отдельный класс для более четкого разграничения.
Теперь давайте разберемся, что же значит каждая из трех частей в названии:
- Model — это логика, которая связанная с данными приложения.
- Другими словами — это POJO, классы работы с API, базой данных и пр.
- View — собственно, это и есть layout экрана, в котором располагаются все необходимые виджеты для отображения информации.
- ViewModel — объект, в котором описывается логика поведения View в зависимости от результата работы Model. Можно назвать его моделью поведения View. Это может быть как форматирование текста, так и логика управления видимостью компонентов или отображения состояний, таких как загрузка, ошибка, пустые экраны и т.д. Также в ней описывается поведение, которое было инициировано пользователем (ввод текста, нажатие на кнопку, свайп и т.п.)
Что же это дает нам в конечном итоге?
- Гибкость разработки. Этот подход повышает удобство работы в команде, т.к. пока один член команды работает над компоновкой и стилизацией экрана — другой, в это время, описывает логику получения данных и их обработки;
- Тестирование. Такая структура упрощает написание тестов и процесс создания mock-объектов. Также, в большинстве случаев отпадает потребность в автоматизированном UI-тестировании, т.к. можно обернуть unit-тестами сам ViewModel;
- Разграничение логики. За счет большего разграничения код становится более гибким и простым в поддержке, не говоря о его читабельности. Каждый модуль отвечает за свою конкретную функцию и только.
Так как ничего идеального не бывает, есть и недостатки:
- Для небольших проектов этот подход может быть неоправданным.
- Если логика привязки данных слишком сложная — отлаживать приложение будет немного труднее.
И все же — кто есть кто?
Изначально, в Android этот паттерн нуждается в небольшой модификации. Точнее сказать, нужно пересмотреть сами компоненты и их привычное восприятие.
Рассмотрим, к примеру, Activity. У нее есть layout-файл (XML) и связанный с ней Java-класс, в котором мы описываем все, что касается её работы. Получается, что xml-файл — это View, а java-класс, соответственно, ViewModel? Похоже на то, но как бы не так. А что, если я скажу, что наш класс — это тоже View? Ведь у custom view тоже есть xml и класс-обработчик, но он считается одним целым? Более того, как в активности, так и в custom view и вовсе можно обойтись без xml-файла, при этом создавая все необходимые виджеты из кода. Вот так и получается, что в нашей архитектуре View == Activity (т.е. XML + Java-class).
Но что же тогда ViewModel и, самое главное, где его размещать? Как мы могли видеть в одном из разделов предыдущей статьи, это совершенно отдельный объект. И именно его мы передавали в xml-файл используя binding.setViewModel() . В нем-то и будут поля и методы, которые нужны нам для связывания моделей с View.
Model же у нас никак не отличается от традиционного её понимания. Единственное, что хотелось бы добавить от себя — не делайте обращения к базе или API прямо во ViewModel. Вместо этого для каждого VM лучше создавать Repository — тогда код будет чище и менее громоздкий.
Таким образом мы получаем следующее: класс активности «обслуживает» только ту логику, которая относится непосредственно к View, но никоим образом не касается его поведения. К таким случаям можно отнести установку Toolbar или надстройки над TabLayout и Viewpager. Немаловажно, что только из View можно обращаться к виджетам напрямую по id ( binding.myView.doSomething() ), т.к. VM не должна знать совершенно ничего о View — коммуникация между ними реализовывается только посредством Binding. Самой же логикой загрузки данных и их отображения занимается ViewModel, а алгоритм получения данных описывается, соответственно, в Model.
Наш дизайнер ушел в отпуск, поэтому схема будет с элементами новогоднего настроения 🙂
Делать — так наверняка
Давайте перейдем непосредственно к реализации. Посмотрев на схему выше можно заметить, что View передает ViewModel не только команды (действия пользователя), но и её жизненный цикл. Почему? Потому что, отчасти, это тоже своего рода действия, которые инициируются пользователем. Ведь именно из-за его действий окно меняет свои состояния. А нам, в свою очередь, необходимо на это реагировать для корректной работы приложения. Решение напрашивается само собой — необходимо делегировать нужные нам колбэки на VM.
Представим, что нужно загружать информацию каждый раз, когда пользователь возвращается на активность. Для этого нам нужно вызывать метод загрузки данных в onResume( ).
Изменим ProfileActivity:
И определим этот же метод в ProfileViewModel:
Теперь данные будут обновляться каждый раз, когда пользователь будет возвращаться на окно. К тому же, если информация еще не была получена до этого — покажется соответствующее состояние. Все очень просто 🙂
В точности делаем и с остальными нужными нам методами. Естественно, определять это каждый раз при создании VM нецелесообразно, поэтому вынесем эту логику в базовые классы. Назовем их BindingActivity и ActivityViewModel:
Теперь по аналогии со стандартным поведением Activity для того, чтобы реагировать на интересующие нас изменения, нам нужно просто переопределить нужный метод.
Как по мне, так нет никакой необходимости каждый раз при создании активности описывать создание биндинга и подвязку к нему VM. Эту логику также можно вынести в базовый класс, но немного изменив привычный нам метод onCreate() . Адаптируем его для получения VM при создании активности и добавим еще пару абстрактных методов для необходимых параметров:
Осталось еще сделать базовый класс для ActivityViewModel. Здесь все проще — добавим в него только экземпляр Activity. Он пригодится нам для создания интентов а также подойдет в качестве контекста:
На этом с активностями всё — у нас есть все необходимые инструменты для описания логики, за исключением одной неприятной мелочи. Такие поля как “viewModel” и “binding” в базовой активности явно типизированы, что усложнит работу с ними, вынуждая каждый раз приводить типы. Поэтому обобщим наши классы следующим образом:
Готово! В конечном итоге мы получили такой вот класс активности:
getVariable() — должен возвращать название переменной, которое указано в тэге data->variable xml-файла активности, а getLayoutId() — должен вернуть тот самый xml. При этом также стоить отметить, что ProfileViewModel должен наследовать ActivityViewModel.
В реализации подобных классов для фрагментов есть небольшие отличия, но подробно рассматривать реализацию в рамках статьи мы не будем, т.к. концепция у них всё же схожа. Готовый класс можно будет увидеть ниже.
Несколько полезных примеров
С момента нашей последней статьи о DataBinding библиотека не только потеряла статус “бета”, но и обросла некоторыми очень полезными нововведениями. Одним из таких является двустороннее связывание (two-way binding). Это когда не только данные влияют на UI, но и наоборот. К примеру, когда пользователь вводит свое имя в EditText, значение сразу обновляется и в переменной. Ранее мы уже делали подобную фичу, однако для этого привлекались TextWatcher и BindingAdapter. Теперь же достичь этого можно намного проще (я бы даже сказал проще некуда). Все, что для этого нужно — изменить
android:text=»@
Мы добавили здесь ViewModel для view в диалоге, т.к. передача ObservableField напрямую в variable не работает корректно (не знаю, баг это или фича, но то, что это не очевидно — факт). Точно также можно привязываться и к другим атрибутам, таким как checked у CheckBox и RadioButton, enabled и пр.
Если вам нужно как-то реагировать или менять данные во время их ввода/вывода — вы можете переопределить методы get() и/или set() у Observable-поля и там производить нужные манипуляции.
А если задача стоит только в отслеживании изменений — можно добавить OnPropertyChangedCallback:
Еще одна фича — это возможность использовать сеттеры как атрибуты в разметке. Допустим, у нас есть метод setAdapter() у того же RecyclerView. Для того, чтобы установить его, нам нужно обращаться непосредственно к экземпляру виджета и вызывать его метод прямо из кода, что противоречит нашему подходу. Для решения этой проблемы можно создавать BidningAdapter, или вообще CustomView, который будет расширять RecyclerView и в нем добавлять свои атрибуты. Но, согласитесь, это не лучший вариант.
К счастью, все намного проще — благодаря кодогенерации мы можем указывать в xml название сеттера при этом просто опуская “set”. Таким образом, задать адаптер можно вот так:
Префикс “bind” — этот все тот же “appliaction namespace”, и, если он уже объявлен, их лучше попросту дублировать для того, чтобы не путать объявленные кастомные атрибуты с атрибутами, сгенерированными с помощью биндинга:
Тем не менее, идея с CustomView имеет право на жизнь в случае, если нужного сеттера в виджете нет (или он назван по неподходящему нам формату).
Возможно кто-то из вас уже задался вопросом о том, как в при такой архитектуре передавать параметры во ViewModel? Здесь все также применяется подход с делегированием, но для удобства мы создаем статический метод open (или openForResult) в котором перечисляем все необходимые параметры. Дальше достаем их и передаем во ViewModel, в котором есть соответствующий конструктор. Например, передадим нашей активности статус как параметр:
Еще одна небольшая наработка, которой хотелось бы поделиться — это вынесение полей “isLoading” и “isError” в базовый класс ViewModel. Эти поля публичные и относятся к типу ObservabeBoolean — благодаря этому нет необходимости дублировать логику состояний загрузки и ошибки, а реагировать на их изменение можно простым include:
При необходимости можно вынести сообщения и иконки для разных кейсов (например, разные причины ошибки) в отдельные variable и таким образом получить гибкий компонент, который применяется парой строк в любом лэйауте.
Забудем о boilerplate!
За время использования MVVM в разработке мы столкнулись с тем, что нам приходилось писать много надоедливого кода: модификация Activtiy/Fragment под базовые классы, прописывание длинных названий Binding-классов в дженериках, создание и связывание ViewModel, а на ранних стадиях и вовсе приходилось копировать базовые классы из проекта в проект, что также занимало драгоценное время. Именно поэтому мы создали библиотеку и плагин для Android Studio, с помощью которых эта рутина стала занимать всего 2-3 клика.
Библиотека AndroidMvvmHelper — это набор базовых классов для удобной работы с MVVM. В этот перечень входят классы как для работы с Activity (BindingActivity и ActivityViewModel), так и с Fragment (BindingFragment и FragmentViewModel), в которых уже реализована логика связывания, а также определены необходимые методы для получения callback-ов. Для того, чтобы начать ее использовать — необходимо просто определить зависимость в grdale-файле:
Хотя решение с библиотекой и упрощает жизнь, создание классов все еще достаточно трудоемкий процесс. Для решения этой задачи мы разработали плагин для IntelliJ IDEA и Android Studio — MVVM Generator. Он позволяет в один клик создать класс BindingActivity (или BindingFragment), его ViewModel, подготовленный xml-файл для разметки и зарегистрировать компонент в AndroidManifest (в случае активности, конечно же). Кроме того, если плагин не обнаружит зависимости библиотеки MVVMHelper, она будет автоматически добавлена.
Чтобы его установить, нужно зайти в раздел управления плагинами:
В нем нажать кнопку “Browse repositories” для поиска доступных плагинов в сети:
В поле поиска вводим “MVVM Generator”, выбираем найденный плагин и жмем “Install”:
По окончанию установки необходимо перезапустить IDE. После этого плагин готов к работе.
Теперь давайте создадим фрагмент профиля. В случае, как и при создании обычного класса, вызываем контекстное меню на нужном пакете и выбираем “Create Binding Fragment”
После того, как мы введем название фрагмента (в нашем случае это “ProfileFragment”) мы получим следующее:
Заглянув внутрь, мы увидим готовые к работе классы:
Помимо этого у нас сразу готов xml:
И все это буквально за пару секунд! Итак.
Резюмируя
На сегодняшний день плагин очень простой и решает только главную задачу — генерацию файлов. В планах присутствуют добавление проверки на наличие биндинга, более гибкая валидация названий, конфигурация расширенных шаблонов активностей и фрагментов и много другое, но пока — имеем то, что имеем.
С момента выхода стабильной версии DataBinding наша команда уже успела реализовать несколько проектов с помощью этого подхода. Из собственного опыта могу лишь сказать, что мне не хочется возвращаться к более традиционным способам написания приложений, а когда все же приходится это делать — чувствуешь себя человеком из будущего. В целом рутинной работы стало меньше, а потому и процесс разработки стал интересней. К тому же, ребята из Google активно работают над адекватной поддержкой этой технологии в Android Studio, что значительно минимизирует неудобства при разработке. Теперь же — это основной подход, который используется нами при создании приложений.
Надеемся, что наш опыт поможет упростить жизнь при создании MVVM-архитектуры в Ваших приложениях так же, как это помогает нам 🙂
Источник