- Android arch exploration: MVVM + flow + UseCases with a UI gate (combine use case’s output to generate the UI)
- The goal of this architectural approach
- What is a simple to medium complexity feature
- Show me the code
- The repository
- Use cases to encapsulate all screen functionalities
- The ViewModel and the UI gate
- UI layer, nothing fancy
- Понимание MVVM паттерна для Android в 2021 году
- В чём проблема?
- Шаблон проектирования
- ПОКАЖИТЕ КОД!
- Использование шаблона MVVM (Model-View-ViewModel) в Android
Android arch exploration: MVVM + flow + UseCases with a UI gate (combine use case’s output to generate the UI)
Looking for an architecture which can work fine for simple to medium features based in MVVM, Clean architecture and unidirectional flow of information.
We’ve started a phase of experimentation in the team to update the architecture approach that we use for most of the features. We are currently in MVVM with a few details here and there but we aim to create something more detailed which can be used for most screens of our app. We love MVVM and remaining in Google’s side of recommendations, but there is still plenty of room for decisions in there.
The goal of this architectural approach
I have been with an idea roaming around my head for a while, which is to have every UseCase exposing it’s result as a Flow and use combine to merge all the use case’s flows together, so there is one single point in charge of generating the UI data which will be then posted to the UI layer. This point is easy to test and far from side effects. This is what the title claims to be the UI gate (every opportunity to put a name on something is always welcome). Before jumping into detail on how this idea works, the pros and the cons, let take a look to what else this architectural approach has:
- MVVM + Clean architecture with UseCases
- Unidirectional flow of information
- Coroutines + flow
- One single LiveData (could be an stateFlow) to expose UI data wrapped in a sealed class to represent data state (loading, error, success).
- An event LiveData to communicate single use events to the UI, like displaying popups to the user, navigating events, permission request, things like that.
- Every request may fail, so prepare error state and retry mechanisms for all of them
What is a simple to medium complexity feature
We are trying to solve a screen which needs to load some info from the internet to be displayed to the user and, once loaded, the user can interact with that information triggering a different request whose response will produce changes in what is displayed to the user. The arch approach supports having more actions from the user, but we’ll keep it in one for simplicity. This screen should cover a wide range of the screens we usually develop in the team.
These is how our new screen should work:
- Initial data is loaded from network, and it can fail, so the screen has success/error&retry/loading states.
- Once the data is loaded, a new button is displayed to trigger an extra request when clicked. As a good request, it can fail, so the UI must display success/error&retry/loading. These state must not hide all the info previously loaded, we want a good experience for the user
This is how it looks on practice:
Show me the code
The code is divided in domain, data and UI packages but it could be separated in modules for better separation of concerns. Let’s take a look to the different layers from to bottom to the top.
The repository
The repo is responsible from communicating with the data sources, which in this case is just network, but could be also db if we had persistence.
This repo will expose flows which will wrap the state the data is at the moment. We’ll use the following sealed class to wrap that state:
In a real scenario we’d have a sealed class as well defining which kind of error we had (such as “NetworkFailure”, “InvalidCredentials”, etc)
The flows exposed by the repo represent perfectly the state of any request. Those states will become handy later to generate the UI depending on the state of each request. This is our repository:
To simulate errors and loading times, our BackendApi is not the classic Retrofit API but a fake API. You can take a look here
Use cases to encapsulate all screen functionalities
There are two features in our screen. The first one is to get data from a network source (our Backend API) and display it to the user. The second one is to react to the user tapping a button to trigger a new request which will return updated new information (the count of done actions).
An important note is that every request might fail, so we want a way to retry any use case. For this, we’ve created a base UseCase which wraps all the retry logic and exposing a flow to the upper layer.
A MutableStateFlow is used as a trigger to launch the logic for the use case. This way, we have a retry mechanism which we can call every time we want. Also, the flatMapLatest will cancel ongoing request when called.
After having this base UseCase, to get the main data for the screen looks like this:
And the UseCase to perform the user action is the following:
You might have noticed that this use case is extending a different base use case. This is because while `MainUseCase` will be always executed to get the main information to display in the screen, this second one is optional, the user might trigger it or might not. Because of our intention of combining all the flows, we need a new base use case type (`NullableResultUseCase`) which will return a flow with null data until the user has triggered the use case. This match the nature of the use case, which is in the end optional for the screen. If you don’t like nulls in Kotlin, we could opt by a more elegant solution (an enum or a Sealed class).
You can take a look to it here
The ViewModel and the UI gate
The ViewModel will be responsible from combining our two use case’s outputs and generate the UI in one single method. The main advantage for this approach is that you can test really easy that method to generate the UI.
This ViewModel main points are:
- Exposes one single LiveData with all that’s needed to render the UI. Outcome indicates if the whole screen is in loading, error&retry or success state. Data within (UiData in this case), should have everything needed to render the success state. In our case, there is an action which can be also in loading/error/success state, to we represent it with an inner Outcome.
- Exposes an eventLiveData, for single use events like displaying a toast or open a new screen. It’s not used for now, but might extend it in the future.
- All of the useCase’s flows are combined together, so the UI is generated in one single point. If there would be more actions that the user can do in this screen, we would have more useCases combined here. Having one single point to do this allows to easily test this method, but it can get complex with a lot of use cases. That’s why the title of the post say simple to medium screens.
This single method in charge of rendering is inspired by Redux or MVI. Having a single “render” method allows you to easily identify issues and reproduce them. By logging (or storing) render events we can replay them and reproduce issues. - Exposes methods to trigger the use cases. Triggering one of the useCases will cause the UI to regenerate, since one of the flows exposed by the use cases will change. Here is the unidirectional flow of information, a user action will trigger the viewModel, which will trigger a useCase, regenerating the UI.
UI layer, nothing fancy
Lastly, the UI layer, where we keep it simple and have a fragment listening to the liveData’s. There is normal XML layout now, but it could be migrated to compose. The success/error&retry/loading state logic has been moved to a base fragment for reutilization, but nothing worth showing. You can take a look to the code below.
Источник
Понимание MVVM паттерна для Android в 2021 году
Я почти на 100% уверен, что вы слышали слова MVC, MVP, MVVM, MVI, MV и так далее, когда искали какую-то информацию для разработчиков. Чтобы понять MVVM, нам нужно перейти к основам. (Не волнуйтесь, я сразу перейду к делу). Оригинал!
В чём проблема?
Когда мы начинаем путь Android разработчика, то размещаем всю логику внутри наших Activity, Fragments, Views и т.д.
И в конечном итоге наши вьюшки делают больше, чем просто отрисовка UI. Они теперь могут сохранять данные в SharedPreferences, базы данных или даже совершать сетевые запросы и делать это всё из одного места.
Шаблон проектирования
Шаблон проектирования или паттерн (англ. design pattern) в разработке программного обеспечения — повторяемая архитектурная конструкция, представляющая собой решение проблемы проектирования в рамках некоторого часто возникающего контекста. Википедия.
Это если кратко. Подробнее можете узнать сами, есть много тематических ресурсов.
Итак, мы определили проблему и знаем, что у нас есть всё необходимое для решения.
V – это View. Activity, Fragment или даже Compose. ViewModel соответствует нашей ViewModel из Jetpack, тут мы отслеживаем изменения конфигурации.
Хорошо, давайте соберем все вместе, наш View подписывается на наблюдаемые объекты ViewModel и реагирует на любые изменения модели Model. Наконец! появилось наше M – Model. Но что… что моя модель такое?
Погодите, так что или кто ваша «Модель» такое? Большинство ресурсов вам скажут, что ваша модель — это «место», где вы можете получить данные, где находится ваш репозиторий и т. д. Я думаю, что это ошибочное определение, и я скажу вам почему.
Это выдержка из этого блога из удивительной Iveta Jurčíková.
В концепте модели нет ничего нового. Она была определена Трюгве Реенскаугом ещё в 1979 как часть MVC архитектуры.
«Модель отвечает за представление состояния, структуры и поведения ментальной модели пользователя».
«View представляет информацию, которую оно получает из одного или нескольких объектов модели».
«Позволяем View как зависимую от модели, и позволяем модели отправлять соответствующие сообщения подписчикам при каждом изменении.»
В итоге мы получили что-то вроде этого:
Модель отражает текущее состояние View, которое может быть загрузкой, успешным или неудачным. И наше View должно рисовать UI в зависимости от состояния.
ПОКАЖИТЕ КОД!
Предположим, нам нужно показать список фильмов в нашем приложении. Состояние мы можем изобразить следующим классом:
Источник
Использование шаблона MVVM (Model-View-ViewModel) в Android
Автор: Антон Валюх, Senior Mobile Developer.
В этой статье поговорим о теории и практике использования очень удобного шаблона проектирования MVVM (Model-View-ViewModel) при разработке Android-приложений.
MVP — Model-View-Presenter
Для начала — немного теории. Всё началось с того, что многие думали, как приспособить шаблон MVC (Model-View-Controller) для написания приложений с интерфейсом пользователя. И в 2006 г. в работе “GUI Architectures” Мартин Фаулер подробно рассмотрел шаблон, который впоследствии получил название “MVP” (“Model-View-Presenter”).
Итак, MVP — шаблон проектирования, производный от MVC, разработанный в основном для построения пользовательского интерфейса. MVP используется для облегчения автоматического модульного тестирования и улучшения разделения логики и отображения.
В этом шаблоне есть три элемента:
- View.
- Presenter.
- Model (модель).
Вот как это всё работает:
- Элемент View отвечает за показ ползовательских данных и перехват пользовательских действий. Всё это он посылает Presenter.
- Presenter обрабатывает действия пользователя в UI, учитывает изменения данных в Model и посылает эту информацию View. Presenter — это элемент, который содержит всю бизнес-логику работы с пользовательским интерфейсом.
- Model содержит в себе модели из предметной области, которые отображают знания и данные предметной области вашего приложения. Model посылает информацию об изменении данных Presenter и принимает сообщения от Presenter.
MVP — реализация в Android
MVP позволяет создавать абстракцию представления. Для этого необходимо выделить интерфейс представления с определенным набором свойств и методов.
Теперь посмотрим, это можно реализовать в Android — для этого напишем небольшой «велосипед».
Presenter взаимодействует с View путем использования специального интерфейса, который описывает абстракцию этого View.
Допустим, у нас есть вот такая модель View:
Обратите внимание: не нужно путать эту модель View с тем видом (View), который мы видим на экране. View, который используется в MVP — некая абстракция View. Другими словами, это обобщение поведения нашего View. В MVP View не отвечает за то, как именно всё будет отображаться на пользовательском интерфейсе. Она отвечает за то, как будет вести себя пользовательский интерфейс.
Presenter получает ссылку на реализацию интерфейса, взаимодействует с моделью нашего View, инициализирует его, вызывает все его сообщения, посылает ему какие-то сообщения и т. д. Всё взаимодействие происходит напрямую: у нас есть реализация View, мы вызываем ее методы и получаем некий результат.
Другими словами, Presenter как бы подписывается на события View и по необходимости изменяет данные в Model.
В качестве примера View в нашем случае будет выступать Activity, отвечающее за реализацию поведения SomeScreenView. Роль View может играть не только Activity, но Fragment, Dialog или просто Android View. Для этого ему также необходимо реализовать поведение SomeScreenView. В указанном Activity используется объект типа SomeScreenPresenter, который и выступает в роли Presente в нашем примере. Этому объекту мы предоставляем ссылку на реализацию вашего View, которое взаимодействует с Presenter путем прямого вызова у него необходимых методов. В свою очередь, Presenter вызывает методы, реализованные внутри вашей Activity, потому что она является реализацией вашей View.
Этот простой пример демонстрирует, как MVP позволяет декомпозировать логику, которая до этого целиком находилась в Activity и была связана обработкой данных и действий пользователя. Мы вынесли эту логику в отдельный модуль, и этот модуль мы, к примеру, можем проверить, обыкновенным модульным тестированием. С моей точки зрения это намного проще, чем тестирование нашей UI-функциональности с помощью Robotium, запуска эмуляторов и т. д. Другими словами, мы взяли всю нашу логику из Activity, которая до этого была Contrloller, вынесли в новый элемент Presenter, и теперь мы можем этот элемент спокойно протестировать, не создавая никаких Controller и View. Кроме того, это код можно дополнительно улучшить — например, использовать внедрение зависимостей (скажем, с помощью RoboGuice или Dagger).
Шаблон MVP неплох, но Microsoft придумала шаблон еще лучше — MVVM (Model-View-ViewModel). Этот шаблон очень любят .NET-разработчики, он используется в Silverlight, его реализация есть в AngularJS. MVVM — очень удобный шаблон.
Чем отличается MVVM от MVP?
MVVM позволяет связывать элементы View со свойствами и событиями ViewModel. При этом ViewModel — абстракция представления. В MVVM есть:
- View — содержит поля, соответствующие интерфейсу пользователя.
- ViewModel — содержит такие же поля, но в предметной области.
- Собственно, Model.
Свойства View совпадают со свойствами ViewModel/Model. При этом ViewModel не имеет ссылки на интерфейс представления. Изменение состояния ViewModel автоматически изменяет View, и наоборот. Для этого используется механизм связывания данных. Также характерная черта MVVM — двусторонняя коммуникация с View.
Далее я кратко пройдусь по реализациям MVVM под Android, с которыми сталкивался в работе, и рассмотрю достоинства и недостатки каждой. В свое время я отметил для себя три реализации: RoboBinding, ngAndroid, Bindroid. В конце этого обзора кратко остановлюсь на Android Data Binding, который я только начинаю для себя открывать, и который выглядит очень перспективным. Вот, кстати, хороший материал по теме.
RoboBinding
RoboBinding представляет собой MVVM-фреймворк для платформы Android. Он позволяет легко осуществить привязку (binding) атрибутов для любых пользовательских компонентов, сторонних компонентов или виджетов для Android. В итоге можно выкинуть много ненужного кода за счет использования бинов.
RoboBinding — установка
Устанавливать RoboBinding, на мой взгляд, непросто, поскольку он для работы требует Android Annotation Processing Toolkit. Это обусловлено тем, что в основе работы RoboBinding лежит генерации кода на этапе прекомпиляции. При этом код генерируется на основе дополнительных аннотаций, которые содержатся в фреймворке и их нужно чем-либо обработать. Этим и занимается Android Annotation Processing Toolkit.
Честно признаюсь, подключить и настроить APT и RoboBinding у меня получилось раза со второго. Надеюсь, у большинства получится быстрее.
RoboBinding — ViewModel
Так выглядит наша модель представления:
Несмотря на то, что эта модель проаннотирована как “Presentation Model”, это именно ViewModel в концепции шаблона MVVM. Это обыкновенная POJO-модель, которая содержит в себе поля, которые впоследствии будут отображены на вашем View (дальше в примерах будет показано, как это сделать). Чтобы ваши данные отображались в двухстороннем порядке, вам необходимо еще имплементировать интерфейс HasPresentationModelmChangeSupport, где в методе getPresentationModelmChangeSupport вы просто должны вернуть реализацию ChangeSupport’а, который будет менять ваши данные.
В SomeScreenViewModel находятся два поля, которые будут содержать значения отображаемые и получаемые на интерфейсе пользователя, и методы, обеспечивающие доступ к этим полям. Тут же есть метод, который будет отвечать за взаимодействие с пользователем. Как именно это всё будет работать, мы по ходу дела разберемся.
RoboBinding — Layout
В самом View мы используем кастомные атрибуты, чтобы можно было связать поля нашей Model с какими-то конкретными элементами интерфейса. Чтобы всё это заработало, мы подключаем дополнительное пространство имен, после чего указываем кастомные дополнительные поля к нашим элементам интерфейса.
В нашей разметке есть EditText, который с помощью bind:text=»$
«привязывается» к полю private String mUserFirstName нашей SomeScreenViewModel. Теперь любые изменения поля mUserFirstName будут отображены в указанном EditText, а любые изменения данных в этом EditText, будут отображаться в поле mUserFirstName. По такому принципу работает механизм двустороннего связывания данных (data binding) между View и ViewModel.
Кроме того, мы можем обрабатывать данные пользователя. Есть bind:onClick
, содержащий имя метода, а в SomeScreenViewModel есть метод с этим же именем, который будет вызван после нажатия кнопки.
RoboBinding — Activity
Но каким образом Model знает о существовании View, и каким образом View знает о существовании Model? Что именно осуществляет процесс связывания данных? В случае RoboBinding, это класс binder. Ему предоставляется ссылка на Layout, в котором элементы интерфейса содержат кастомные поля, и ему дается ссылка на реализацию Model. После чего binder связывает элементы интерфейса с полями внутри Model. Теперь, воспользовавшись каким-нибудь setter/getter или просто записав в поля данные нашей Model, мы получим их отображение на View.
Всё достаточно просто. При этом, во-первых, нет никаких findViewById. Во-вторых, вы не отвечаете за то, с чем вы конкретно взаимодействуете, — для вас есть только абстрактная модель, в которую вы можете устанавливать значения и получать их оттуда.
Теперь поговорим, как конкретно это работает. Например, пользователь нажал кнопку на интерфейсе пользователя. При этом сработало событие, updateUser, подвязанное к этой кнопке с помощью bind:onClick=»updateUser»
(см. RoboBinding — Layout). Это приводит к вызову связанного с этим действием метода updateUser () в SomeScreenViewModel (см. RoboBinding — ViewModel) Не забываем, что наш SomeScreenViewModel содержит реализацию PresentationModelChangeSupport, о которой мы говорили до этого. Это необходимо, чтобы отображать состояние вашей Model на вашу View.
Со стороны, это можно представить следующим образом:
— Эй, объект типа PresentationModelChangeSupport, возьми поле, которое называется «userFullName» и обнови его! — «говорим» мы в методе updateUser().
— Хорошо, — «думает» объект типа PresentationModelChangeSupport — «userFullName», подвязывается в bind:text=»
. — А есть у меня где-то геттер, который называется «getUserFullName»? Есть. Вызываю его, получаю значение (которое равно mUserFirstName + » » + mUserLastName), и это значение отображаю в
Именно таким образом работает реализация двустороннего связывания данных в RoboBinding.
RoboBinding — преимущества и недостатки
Достоинства RoboBinding:
- двунаправленное (двустороннее) связывание;
- генерация кода;
- поддержка списков.
Недостатки:
- проблемы с библиотекой AppCompat;
- нет поддержки RecyclerView.
Двустороннее связывание: знак “$” в коде означает, что, при изменении данных в Model, они будут отображены во View и, при изменении данных во View, они будут спроецированы в Model. Если же знака “$” нет, это значит, что данные из Model будут отображены во View, но не наоборот.
Работа RoboBinding основана на генерации кода. То есть на этапе предварительной компиляции — на основе аннотаций, что вы расписали в классе, будет сгенерирован необходимый код, после чего он будет скопмилирован. Это значит, что на этапе выполнения никаких дополнительных затрат вам не потребуется.
Кроме того что вы можете связывать простые данные, вы можете связывать и списки (коллекции), а это здорово. Кроме того, есть поддержка работы с ListView: вы можете завязать какую-либо коллекцию на ListView, и всё будет хорошо работать (по крайней мере, у меня работало).
Что касается недостатков, если RoboBinding со списками работает, то с RecyclerView — нет, по крайней мере, пока еще.
Также есть проблема с библиотекой совместимости, потому что связывание построено на кастомных атрибутах. Это значит, что если у нас идут элементы интерфейса из библиотеки совместимости, и вы в неё пытаетесь добавить кастомный атрибут — это работает не всегда, а если и работает, то очень плохо. На сайте RoboBinding’а все эти баги уже отмечены — наверняка работа над их исправлением уже идёт, так как RoboBinding развивается, и развивается достаточно быстро.
ngAndroid
Следующая библиотека, которая мне понравилась, — ngAndroid, основанная на идеях JavaScript-фреймворка AngularJS (но только на идеях — никакого JavaScript здесь нет). Работает она очень похоже на RoboBinding.
ngAndroid — установка
В отличие от RoboBinding, ngAndroid устанавливается очень просто, и всё работает с первого раза: compile ‘com.github.davityle:ngandroid:0.0.4’.
ngAndroid — Model
Model практически ничем не отличается — это обыкновенные данные, способы доступа к данным. Здесь нету action — они поддерживаются, но не в таком виде.
ngAndroid — Layout
Layout такой же — отличия минимальные. Точно так же работа построена на основе кастомных атрибутов — подключили нужное нам пространство имён и отобразили модель с именем атрибута внутри этой модели: модель — имя атрибута, модель — имя атрибута… Также есть поддержка событий пользовательского интерфейса, но об этом потом.
ngAndroid — Activity
Различия начинаются в Activity. Рассмотрим пример, в котором есть некая Activity (здесь может быть Fragment, View и т. д.). А в этой Activity используется аннотация @NgScope, чтобы ngAndroid знал, что эта View должна содержать binder. Внедряем ViewModel нашего примера (SomeScreenViewModel) в Activity с помощью аннотации @ NgModel. Вот, собственно говоря, и все: указали View, указали ViewModel. В отличии от RoboBinding, на этом этапе работы внутри ngAndroid включается инжектор, который выполняет необходимое внедрение указанных зависимостей и настраивает их.
Таким образом реализовывается двустороннее связывание. При этом в роли класса, который отвечает за обработку событий, выступает сама Activity. В Activity реализован метод updateUser(), который до этого был привязан в файле с разметкой к кнопке. Из примера также видно, что, в отличии от RoboBinding, в ngAndroid updateUser() находиться в Activity а не в ViewModel.
ngAndroid — преимущества и недостатки
Достоинства:
- двунаправленное связывание;
- проект на стадии активной разработки.
Недостатки:
- нет поддержки списков / RecyclerView;
- используется рефлексия (обещают перейти на генерацию кода);
- проект на стадии активной разработки, поэтому достаточно сырой.
NgAndroid сейчас быстро развивается — кроме моделей и кликов, библиотека поддерживает long-click, change, disable и т. д. Поддерживаемых директив становится всё больше и больше. В то же время, такое быстрое развитие можно рассматривать как недостаток — я бы поостерегся пока использовать ngAndroid на работе.
На данный момент поддерживаются следующие angular-директивы:
- NgMode
- NgClick
- NgLongClick
- NgChange
- NgDisabled
- NgInvisible
- NgGone
- NgBlur
- NgFocus
В целом библиотека очень понравилась — прозрачностью, простотой, тем, что есть внедрение зависимости (меньше кода, и это хорошо), тем, что есть двустороннее (двунаправленное) связывание.
Bindroid
Bindroid — еще одна реализация шаблона MVVM для Android-приложений. Bindroid представляет собой библиотеку с открытым исходным кодом, основная целью которой — упрощение связывания пользовательского интерфейса и данных. В ее основе лежит шаблон «Наблюдатель» (Observer) для работы с моделями, а также набор методов для быстрого связывания этих объектов и интерфейсов пользователя.
Bindroid — Model
Bindroid в корне отличается от уже рассмотренных реализаций тем, что в нем нет кастомных полей ваших UI-атрибутов, другими словами, нет элемента, который связывает ваш View c полями вашей Model. Вместо этого есть поле TrackableField, которое находится внутри Model — все поля данных должны быть TrackableField’ом. Это сделано, чтобы, когда вы измените какое-либо поле, оно изменилось на вашей View. Таким образом, здесь реализован шаблон Observer, который следит, чтобы изменения данных отображались в UI.
Bindroid — Layout
К сожалению, для связывания необходимо вручную связать каждое поле из Model с конкретной его реализацией внутри вашей View, используя findViewById. Хотя от findViewById можно избавиться, используя ButterKnife или Android Annotations.
Bindroid — Activity
Bindroid — преимущества и недостатки
Достоинства:
- двунаправленное связывание;
- поддерживает работу с библиотекой AppCompat.
Недостатки:
- нет поддержки генерации кода;
- нет проверки во времени компиляции;
- слишком много кода для связывания.
Bindroid может показаться вам интересным, если вы не любите кастомные поля у элементов интерфейса или внедрение зависимости, или если вам не нравится переключать лишние инструменты вроде Android Annotation Processing Toolkit, которые потребляют дополнительные ресурсы. Или, может быть, вам нужно, чтобы всё быстро компилировалось и работало. Тогда Bindroid вам подойдет, но надо будет писать код чуть подольше.
На мой взгляд, большой недостаток — отсутствие проверки на этапе компиляции. Например, если в Model поле называется userLastName, а в Activity вы допустите ошибку, всё у вас скомпилируется, но в процессе выполнения произойдет exception. И, поскольку Stack Trace будет у вас очень веселым при выполнении связывания, будете очень долго искать, что не так. Это — серьезный недостаток.
Android Data Binding
Весной 2015 г. Google на Google I/O представил библиотеку Android Data Binding, пока что доступную в бета-версии. Возможностей у нее много, но в в статье расскажу о ее возможностях, связанных с MVVM.
Android Data Binding -—установка
Установка достаточно простая. Здесь стоит сказать, что, поскольку Android Data Binding находится в стадии бета-тестирования, Android Studio пока не поддерживает нормальную работу с (июль 2015, Android Studio v 1.3.0).
Android Data Binding — Model
В Model ничего необычного нет — у нас есть те же самые поля и есть методы доступа к этим полям.
Android Data Binding — Layout
Что касается файла разметки, нашего View, тут уже есть серьезные отличия от рассмотренных ранее реализаций. Во-первых, корневой узел теперь у нас — так называемый Layout. В разделе data указывается модель и то, как она будет называться (пространство имен). А дальше происходит отображение данных из UI на поля указанной модели (в данном случае это user.fullName, user.firstName и user.lastName
— соответственно, те же поля внутри вашей модели).
То есть, как и раньше, у нас есть поля, есть модель и есть механизм связывания, который позволяет отобразить поля вашей модели на элементы UI интерфейса. Разница заключается в том, что корневым узлом у вас является Layout, и, кроме самого Layout, у вас есть ещё секция с данными, где вы должны указать, какую модель вы используете.
Детальней использование можно рассмотреть на следующем примере.
Android Data Binding — Activity
В Activity минимальные изменения: сделали модель данных, связали View и модель данных, после чего, в процессе изменения каких-то значений внутри модели, эти данные будут изменяться внутри View. Если данные будут изменены на View, изменения будут доступны в модели. Для двустороннего связывания между данными и местом их отображения в UI используется символ «@» (например, android:text=»@
). В противном случае, реализация связывания получиться односторонней.
Таким образом, использование Data Binding, с моей точки зрения, выглядит достаточно простым и прозрачным, и является реализацией шаблона MVVM.
Android Data Binding — возможности
Кроме того, что в Data Binding есть возможность реализовать шаблон MVVM, у этой технологии есть еще много хороших возможностей (на самом деле, Android Data Binding — тема для отдельного доклада или статьи).
Язык выражений. Позволяет писать примитивную логику внутри вашей View. Главное — не переусердствовать, чтобы логика не перешла во View. Тем не менее, язык выражений позволяет делать упрощения — в зависимости от состояния, вы можете подхватывать разные обработчики, делать форматирование. Это очень удобно.
Импорты. Можно дополнительно импортировать любые классы: к примеру, импортировали View, и дальше можете свойство этого класса использовать в каких-то своих выражениях.
Поддержка ресурсов. Написали выражение, указали, какие ресурсы брать, и всё прекрасно работает.
Можно создавать кастомные бины.
Можно долго перечислять — много чего еще есть.
Наверняка рано или поздно Android Data Binding станет новым стандартом создания Android-приложений — я в этом уверен почти на все 100.
Android Data Binding — преимущества и недостатки
Достоинства:
- официальная библиотека от Google;
- генерация кода;
- проверка во время компиляции;
- простота в использовании и расширении;
- новый Android-стандарт.
Недостатки:
- нет поддержки двунаправленного связывания (пока еще);
- нет поддержки IDE (пока еще);
- много ложных ошибок в Android Studio (но все компилируется и запускается).
Важный недостаток — отсутствие полноценного двунаправленного связывания — оно есть, но пока работает плохо. Ну, что же взять с бета-версии… наверняка, это первое, что будет полноценно работать в ближайшем будущем.
Нет поддержки IDE — как результат, очень много ошибок в Android Studio. Но всё компилируется, всё запускается, всё работает. Если кому-то интересно и хотите подключить, думаю, не пожалеете.
Источник