Спикер:
Тема:
Антон Валюх
Использование паттерна MVVM в Android
MVP
MVP (Model-View-Presenter) — шаблон проектирования, производный от MVC, который был разработан в основном для построения пользовательского интерфейса.
Используется для облегчения автоматического модульного тестирования и улучшения разделения логики от отображения. Основные принципы работы с шаблоном MVP, были подробно проанализированы в статье Мартина Фаулера «GUI Architectures» (2006).
http://martinfowler.com/eaaDev/uiArchs.html
MVP
View Presenter Model
user events updates model
state-change eventupdates view
View это интерфейс пользователя, который отображает данные и выполняет маршрутизацию пользовательских команд (событий) в Presenter.
Presenter управляет данными (Model) и представлением (View). Он извлекает данные из модели и форматирует их для отображения во View.
Model предоставляет данные для пользовательского интерфейса.
MVP
public interface SomeScreenView { void startLoading(); void stopLoading(); void mapDataItems(final Collection<DataItem> items);}
MVP позволяет создавать абстракцию представления. Для этого необходимо выделить интерфейс представления с определенным набором свойств и методов.
MVP
public class SomeScreenPresenter extends Presenter { private SomeScreenView mView; public void setView(SomeScreenView view) { mView = view; } @Override public void initialize() { mView.startLoading(); mView.mapDataItems(...); mView.stopLoading(); }}
Presenter, получает ссылку на реализацию интерфейса, подписывается на события представления и по запросу изменяет модель.
Presenter взаимодействует с View путем использования специального интерфейса, который описывает абстракцию этого View.
MVP
@EActivity(R.layout.activity_some_screen)public class SomeScreenActivity extends Activity implements SomeScreenView { private SomeScreenPresenter mPresenter; @ViewById(R.id.drawer_layout) protected ProgressBar mProgressBar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mPresenter = new SomeScreenPresenter(this); mPresenter.initialize(); }}
View взаимодействует напрямую с Presenter-ом, путем вызова соответствующих функций или событий экземпляра Presenter.
MVVM
MVVM
View ViewModel Model
UI eventsModel change
events
Update
ViewModel data
Property hanged events
Read
MVVM позволяет связывать элементы View со свойствами и событиями ViewModel. ViewModel — это абстракция представления. Свойства View совпадают со свойствами ViewModel/Model. При этом ViewModel не имеет ссылки на интерфейс представления.
Изменение состояния View-модели автоматически изменяет представление и наоборот. Для этого используется механизм связывания данных (Bindings).
Для MVVM характерна двухсторонняя коммуникация с представлением.
MVVM
• RoboBinding
• ngAndroid
• Bindroid
• Android Data Binding
RoboBinding
Представляет собой MVVM фреймворк для платформы Android.
• позволяет легко осуществить привязку (binding) атрибутов для любых пользовательских компонентов, сторонних компонентов или виджетов для Android. Как результат позволяет «выкинуть» много «ненужного кода» за счет использования бинов.
• не уменьшает производительность за счет использования генерации кода вместо рефлексии.
RoboBinding - установка
apply plugin: 'com.android.application'apply plugin: 'com.neenbedankt.android-apt'
apt("org.robobinding:codegen:$robobindingVersion") { exclude group: 'com.google.android', module: 'android'}
compile("org.robobinding:robobinding:$robobindingVersion") { exclude group: 'com.google.android', module: 'android'}
buildscript { dependencies { classpath 'com.android.tools.build:gradle:xxx' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' }}
RoboBinding - ViewModel
@PresentationModelpublic class SomeScreenViewModel implements HasPresentationModelmChangeSupport {
private PresentationModelChangeSupport mChangeSupport; private String mUserFirstName; private String mUserLastName;
public SomeScreenViewModel() { mChangeSupport = new PresentationModelChangeSupport(this); }
public String getUserFirstName() { return mUserFirstName; } public String getUserLastName() { return mUserLastName; } public String getUserFullName() { return mUserFirstName + " " + mUserLastName; }
public void setUserFirstName(String userFirstName){ mUserFirstName = userFirstName; } public void setUserLastName(String userLastName){ mUserLastName = userLastName; } public void updateUser() { mChangeSupport.firePropertyChange("userFullName"); } @Override public PresentationModelChangeSupport getPresentationModelmChangeSupport() { return mChangeSupport; }}
RoboBinding – Layout
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:bind="http://robobinding.org/android" android:orientation="vertical">
<TextView …… bind:text="{userFullName}"/> <EditText …… bind:text="${userFirstName}"/>
<EditText …… bind:text="${userLastName}"/> <Button …… bind:onClick="updateUser"/>
</LinearLayout>
RoboBinding - Activity
public class SomeScreenActivity extends Activity {
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); SomeScreenViewModel presentationModel = new SomeScreenViewModel();
View rootView = Binders.inflateAndBindWithoutPreInitializingViews(this, R.layout.activity_some_screen, presentationModel);
setContentView(rootView); }
}
RoboBinding
Достоинства:- двунаправленное связывание- генерация кода- поддержка списков
Недостатки:- проблемы с библиотекой AppCompat- нет поддержки RecyclerView
ngAndroid
Основан на идеях JavaScript-фреймворка – AngularJS.
ngAndroid - установка
compile 'com.github.davityle:ngandroid:0.0.4'
ngAndroid - Model
public class Model { private String mUserFirstName; private String mUserLastName; private String mUserFullName; public String getUserFirstName() { return mUserFirstName; } public String getUserLastName() { return mUserLastName; } public String getUserFullName() { return mUserFullName; }
public void setUserFirstName(String userFirstName){ mUserFirstName = userFirstName; } public void setUserLastName(String userLastname){ mUserLastName = userLastname; } public void setUserFullName(String userFullName) { mUserFullName = userFullName; }}
ngAndroid - Layout
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:ng="http://schemas.android.com/apk/res-auto" android:orientation="vertical">
<TextView …… ng:ngModel="model.userFullName" <EditText …… ng:ngModel="model.userFirstName"/> <EditText …… ng:ngModel="model.userLastName"/>
<Button …… ng:ngClick="updateUser()"/> </LinearLayout>
ngAndroid - Activity
@NgScopepublic class SomeScreenActivity extends Activity {
@NgModel SomeScreenViewModel mScreenViewModel;
private final NgAndroid mNg = NgAndroid.getInstance();
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mNg.setContentView(this, R.layout.activity_some_screen); } protected void updateUser(){ String firstName = mScreenViewModel.getUserFirstName(); String lastName = mScreenViewModel.getUserLastName(); mScreenViewModel.setUserFullName(firstName + " " + lastName); }}
ngAndroid – а так же….
Поддерживаемые на данный момент angular директивы:
• NgModel• NgClick• NgLongClick• NgChange• NgDisabled• NgInvisible• NgGone• NgBlur• NgFocus
ngAndroid
Достоинства:- двунаправленное связывание- проект на стадии активной разработки
Недостатки:- нет поддержки списков / RecyclerView- используется рефлексия (обещают перейти на
генерацию кода)- проект на стадии активной разработки, поэтому
достаточно сырой
Bindroid
Bindroid реализация шаблона MVVM для Android приложений. Представляет собой библиотеку с открытым исходным кодом, основной целью которой является упрощение связывания UI и данных. В ее основе лежит шаблон Наблюдатель (Observer) для работы с моделями а так же набор методов для быстрого связывания этих объектов и интерфейсов пользователя.
Bindroid - Model
public class SomeScreenViewModel { private TrackableField<String> mUserFirstName = new TrackableField<String>(); private TrackableField<String> mUserLastName = new TrackableField<String>(); private TrackableField<String> mUserFullName = new TrackableField<String>("Here could be your advertising."); public String getUserFirstName() { return mUserFirstName.get(); } public void setUserFirstName(String firstName) { mUserFirstName.set(firstName);} public String getUserLastName() { return mUserLastName.get(); } public void setUserLastName(String lastName) { mUserLastName.set(lastName); } public String getUserFullName() { return mUserFullName.get(); } public void setUserFullName(String fullName) { mUserFullName.set(fullName); }}
Bindroid - Layout
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical">
<TextView …… android:id="@+id/text_user_fullname" <EditText …… android:id="@+id/edit_user_firstname"/> <EditText …… android:id="@+id/edit_user_lastname"/>
<Button …… android:onClick="updateUser"/> </LinearLayout>
Bindroid - Activity
public class SomeScreenActivity extends Activity {
SomeScreenViewModel mScreenViewModel = new SomeScreenViewModel();
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_some_screen); UiBinder.bind(new EditTextTextProperty(this.findViewById( R.id.edit_user_firstname)), mScreenViewModel, "userFirstName", BindingMode.TWO_WAY);
UiBinder.bind(new EditTextTextProperty(this.findViewById( R.id.edit_user_lastname)), mScreenViewModel, "userLastName", BindingMode.TWO_WAY);
UiBinder.bind(new TextViewTextProperty(this.findViewById(R.id.text_user_fullname)), mScreenViewModel, "userFullName", BindingMode.TWO_WAY);
} public void updateUser(View v){ String firstName = mScreenViewModel.getUserFirstName(); String lastName = mScreenViewModel.getUserLastName(); mScreenViewModel.setUserFullName(firstName + " " + lastName); }}
Bindroid
Достоинства:- двунаправленное связывание- поддерживает работу с библиотекой AppCompat
Недостатки:- нет поддержки генерации кода- нет проверки во временя компиляции- слишком много кода для связывания
Android Data Binding
- анонсирована на Google IO 15;- создана Google;- пока доступна только как beta.
Android Data Binding - установка
apply plugin: 'com.android.databinding'
dependencies { classpath 'com.android.tools.build:gradle:1.3.0-beta2' classpath 'com.android.databinding:dataBinder:1.0-rc0'}
Android Data Binding - Model
public class User { private String mFirstName; private String mLastName; private String mFullName;
public User(String firstName, String lastName) { mFirstName = firstName; mLastName = lastName; } public String getFirstName() { return mUserFirstName; } public String getLastName() { return mLastName; } public String getFullName() { return mFullName; } public void setFirstName(String userFirstName) { mUserFirstName = userFirstName; } public void setLastName(String userLastname){ mUserLastName = userLastname; } public void setFullName(String userFullName) { mUserFullName = userFullName; }
public void updateUser(View v){ mFullName = mFirstName + " " + mLastName; }}
Android Data Binding - Layout
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data> <variable name="user" type="com.example.User"/> </data> <LinearLayout ……>
<TextView …… android:text="{user.fullNameName}"/> <EditText …… android:text="@{user.firstName}"/> <EditText …… android:text="@{user.lastName}"/>
<Button …… android:onClick="updateUser"/>
</LinearLayout></layout>
Android Data Binding - Activity
public class SomeScreenActivity extends Activity {
private User mUser = new User("Anton", "Valiuh");
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
binding.setUser(mUser); }}
Android Data Binding – а так же….
• Язык выражений android:text="@{String.format("Hello %s",viewModel.field )}" android:onClick="@{user.isFriend ? handlers.onClickFriend : handlers.onClickEnemy}“
• Импорты <import type="android.view.View" alias="SomeAlias"/>
... android:visibility="@{user.isAdult ? SomeAlias.VISIBLE : SomeAlias.GONE}“
• Поддержка ресурсов android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}“
• Поддержка коллекций android:text="@{list[index]}" android:text="@{map[`firstName`}“
• Возможность создавать кастомные бины.
И много чего еще.
Android Data Binding
Достоинства:- официальная библиотеки от Google- генерация кода- проверка во время компиляции- простота в использовании и расширении- новый Android стандарт
Недостатки:- нет поддержки двунаправленного связывания (пока еще)- нет поддержки IDE (пока еще)- много ложных ошибок в Android Studio (но все компилируется и запуска)
Ваши вопросы?
Благодарю за внимание!
Top Related