Moxy –
реализация MVP
под Android
С щепоткой магии
Шмаков Юрий
senneco@gmail.com
Чт о т а кое MVP
Основные составляющие: Model, View и Presenter
•  :Плюсы
• ,Код разбивается на мелкие независимые кусочки
• Сильно упрощается написание тестов к коду
• - ,Легко менять какую то часть не ломая при этом другую
• :Минусы
• Кода становится больше
• К этому подходу нужно привыкать
• На данный момент не сильно распространённый
MVP в Andr oi d
Позволяет снять метку GodObject с Activity:
• GUIПолное управление
• Обработка взаимодействия с пользователем
• Запуск асинхронных задач
• Обработка результата асинхронной задачи
• :Дополнительные хотелки
• View должна привязываться к уже имеющемуся Presenter при смене
конфигурации
• View всегда обязана иметь актульаное состояния
• Presenter -должен уметь жить независимо от чего то
Moxy – т е ория
View
ViewState
Presenter Model
Commands
Moxy – т е ория
View
ViewState
Presenter Model
Commands
Moxy – т е ория
View
ViewState
Presenter Model
Commands
Moxy – т е ория
View
ViewState
Presenter Model
Commands
Moxy – т е ория
ViewState
Presenter Model
Commands
View
Moxy – т е ория
ViewState
Presenter Model
Commands
View
Moxy – т е ория
ViewState
Presenter Model
Commands
View
Moxy – т е ория
ViewState
Presenter Model
Commands
View
View
Moxy – инс т руме нт ы
Moxy – MvpPr e s e nt e r
• -Содержит в себе часть бизнес логики
• -Отвечает только за одну логическую единицу бизнес логики
• Типизирован MvpView
• :Имеет полезные методы
• voidattachView(View view) и voiddetachView(View view)
• View getViewState()
• voidonFirstViewAttach()
• boolean isInRestoreState(View view)
Moxy – MvpVi e w и MvpVi e wSt a t e
• MvpView ,описывает команды которые Presenter может передать
во View
• MvpViewState ,хранит команды которые были переданы во View
• MvpViewStateимеет метод voidrestoreState(View view)
• Чаще всего не придётся самостояетльно создавать
MvpViewState
Moxy – @I nj e c t Vi e wSt a t e
• Применяется к классу MvpPresenter
• :Имеет три поведения
• Если указан параметр value, то будет использован указанный ViewState
• Если указан параметр view, то будет сгенерирован ViewState для
указанной View
• ,Если никакой параметр не указан то будет сгенерирован ViewState
для View, которой типизирован Presenter(опасайтесь типизированных
View)
• Позволяет работать с методом View getViewState()
Moxy – St a t e St r a t e gy
• Управляет нахождением команды во ViewState
• Имеет два callback- :метод
• voidbeforeApply(currentState, incomingState)
• voidafterApply(currentState, incomingState)
• Указать стратегию можно аннотацией @StateStrategyType
• @StateStrategyType :можно применить
• Ко всему интерфейсу View
• К конкретному методу интерфейса View
Moxy – MvpDe l e ga t e
• Управляет жизненным циклом Presenter
• Подставляет во View правильный экземпляр Presenter
• Отвечает за привязку View к Presenter
• Имеет несколько методов для получения состяния View:
• voidonCreate(Bundlebundle), voidonCreate() и voidonDestroy()
• voidonStart()
• voidonSaveInstanceState(BundleoutState)
• voidsetParentDelegate(MvpDelegatedelegate, StringchildId)
Moxy – @I nj e c t Pr e s e nt e r
• Применяется к Presenter-полям реализации View
• Сообщает MvpDelegate, какой Presenter нужно использовать
• Может быть двух(с половиной) :типов
• PresenterType.LOCAL
• PresenterType.GLOBAL и PresenterType.WEAK
• , !=В случае если тип PresenterType.LOCAL, MvpDelegateбудет
искать Presenter :по одному из правил
• ,По статичному тэгу указанному в параметре tag
• Используя factory, сгенерирует тэг и сам Presenter, по необходимости
Moxy – Mode l
:Содержит в себе работу с данными
• Получение данных
• Из хранилища
• Из интернате
• Хранение данных
• Обработка данных
,Самое подходящее место чтобы разгуляться DI
Moxy – приме р #1
:Задача сделать экран авторизации
• :По нажатию на кнопку входа
• Показать диалог запроса
• Начать асинхронный запрос авторизации
• :После завершения асинхронного запроса авторизации
• Скрыть диалог прогресса
• ,Если пришла ошибка то показать диалог с ошибкой
• ,Если авторизация прошла успешно то перейти на главный экран
Moxy – приме р #1
:Задача сделать экран авторизации
:Решение
•Сделать SignInView
•Сделать SignInPresenter
•Сделать SignInActivity
Moxy – приме р #1
@StateStrategyType(AddToEndSingleStrategy.class)
public interface SignInView extends MvpView
{
void toggleProgress(boolean show);
void showError(Exception exception);
void hideError();
void onSignIn();
}
public class SignInActivity extends MvpActivity implements SignInView
{
@InjectPresenter
SignInPresenter mSignInPresenter;
...
Moxy – приме р #1
@InjectViewState
public class SignInPresenter extends MvpPresenter<SignInView>
{
@Inject
Repository mRepository;
public SignInPresenter()
{
WagamamaApplication.getAppComponent().inject(this);
}
public void auth(final String login, final String password)
{
getViewState().hideError();
getViewState().toggleProgress(true);
↓↓↓
↓↓↓
mRepository.authentication().signIn(login, password)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<AuthenticateData>()
{
@Override
public void onCompleted()
{
getViewState().toggleProgress(false);
getViewState().onSignIn();
}
@Override
public void onError(Throwable e)
{
getViewState().toggleProgress(false);
getViewState().showError(new Exception(e));
}
});
Moxy – приме р #2
:Задача сделать аудиоплеер
•В приложении есть список треков
• Текущий трек выделяется и имеет кнопку play/ pause
• ,В приложении есть фрагмент в котором отображается
,текущий трек его состяние и кнопки play/ pauseи prev/ next
•Приложение отображает notification с отображением текущего
,трека его состяние и кнопок play/ pauseи prev/ next
Moxy – приме р #2
:Задача сделать аудиоплеер
:Решение
•Сделать PlayerView
•Сделать PlayerPresenter
•Сделать PlaylistAdapter
•Сделать PlayerFragment
•Сделать PlayerService
Moxy – приме р #2
public interface PlayerView extends MvpView
{
String PLAYER_STATE = "playerState";
@StateStrategyType(SingleStateStrategy.class)
void setCurrentTrack(TrackInfo track);
@StateStrategyType(value = PlayerStateStrategy.class, tag = PLAYER_STATE)
void playTrack();
@StateStrategyType(value = PlayerStateStrategy.class, tag = PLAYER_STATE)
void pauseTrack();
}
Moxy – приме р #2
public class PlayerStateStrategy implements StateStrategy
{
@Override
public <View extends MvpView> void beforeApply(List<Pair<ViewCommand<View>, Object>> currentState,
Pair<ViewCommand<View>, Object> incomingState)
{
for (Pair<ViewCommand<View>, Object> viewCommand : currentState)
{
if (viewCommand.first.getTag().equals(PlayerView.PLAYER_STATE))
{
currentState.remove(viewCommand);
break;
}
}
currentState.add(incomingState);
}
@Override
public <View extends MvpView> void afterApply(List<Pair<ViewCommand<View>, Object>> currentState,
Pair<ViewCommand<View>, Object> incomingState)
{
}
}
Moxy – приме р #2
public class PlayerPresenter extends MvpPresenter<PlayerView>
{
public static final String TAG = "player";
public PlayerPresenter()
{
super();
PlayerApp.get().getBus().register(this);
}
@Subscribe
public void playTrack(PlayTrackEvent event)
{
playTrack(event.getTrack());
}
public void playTrack(TrackInfo trackInfo)
{
getViewState().setCurrentTrack(trackInfo);
getViewState().playTrack();
}
↓↓↓
↓↓↓
public void playTrack()
{
getViewState().playTrack();
}
public void pauseTrack()
{
getViewState().pauseTrack();
}
@Override
public void onDestroy()
{
super.onDestroy();
PlayerApp.get().getBus().unregister(this);
}
}
Moxy – приме р #2
public class PlaylistAdapter extends BaseAdapter implements PlayerView
{
@InjectPresenter(type = PresenterType.WEAK, tag = PlayerPresenter.TAG)
PlayerPresenter mPlayerPresenter;
public PlaylistAdapter(MvpDelegate<?> parentDelegate)
{
MvpDelegate<PlaylistAdapter> delegate = new MvpDelegate<>(this);
delegate.setParentDelegate(parentDelegate, "");
delegate.onCreate();
}
...
public class MainActivity extends MvpActivity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
PlaylistAdapter adapter = new PlaylistAdapter(getMvpDelegate());
...
Moxy – приме р #2
public class PlayerService extends Service implements PlayerView
{
@InjectPresenter(type = PresenterType.WEAK, tag = PlayerPresenter.TAG)
PlayerPresenter mPlayerPresenter;
private MvpDelegate<PlayerService> mDelegate;
@Override
public void onCreate()
{
super.onCreate();
mDelegate = new MvpDelegate<>(this);
mDelegate.onCreate(null);
mDelegate.onStart();
}
...
@Override
public void onDestroy()
{
super.onDestroy();
mDelegate.onDestroy();
...
Moxy – приме р #2
public class PlayerFragment extends MvpFragment implements PlayerView
{
@InjectPresenter(type = PresenterType.WEAK, tag = PlayerPresenter.TAG)
PlayerPresenter mPlayerPresenter;
...
Moxy – конкуре нт ы
главный конкурент– одинMosby с неудобным ViewState
Moxy – ит ог о
• :Что имеем
• Решены проблемы с жизненным циклом
• -Всегда отображается актуальное состояние бизнес логики
• Codegeneration
• :Важные советы
• Не меняйте View командой из View
• Добавляйте и удаляйте элементы View только через Presenter
• ,Если не компилится внимательно вчитайтесь в описание ошибки
• Используйте DI для связки Presenter↔Model
• MVP != инструмент
На почит а т ь
• Moxy — MVP Androidреализация под с щепоткой магии
• AndroidApplication Architecture(AndroidDev Summit 2015)
• AndroidTestingCodelab
• Nucleus
• Mosby
• OldMosby
• STINSON'SPLAYBOOK FORMOSBY 
• AndroidReactiveMVP: практика
• AndrtoidClean Architecture 
• . Speaker Clean Architecture MVPАлексей Макаров и  
• Mosby issues 85 

Moxy – реализация MVP под Android. С щепоткой магии

  • 1.
    Moxy – реализация MVP подAndroid С щепоткой магии Шмаков Юрий senneco@gmail.com
  • 2.
    Чт о та кое MVP Основные составляющие: Model, View и Presenter •  :Плюсы • ,Код разбивается на мелкие независимые кусочки • Сильно упрощается написание тестов к коду • - ,Легко менять какую то часть не ломая при этом другую • :Минусы • Кода становится больше • К этому подходу нужно привыкать • На данный момент не сильно распространённый
  • 3.
    MVP в Androi d Позволяет снять метку GodObject с Activity: • GUIПолное управление • Обработка взаимодействия с пользователем • Запуск асинхронных задач • Обработка результата асинхронной задачи • :Дополнительные хотелки • View должна привязываться к уже имеющемуся Presenter при смене конфигурации • View всегда обязана иметь актульаное состояния • Presenter -должен уметь жить независимо от чего то
  • 4.
    Moxy – те ория View ViewState Presenter Model Commands
  • 5.
    Moxy – те ория View ViewState Presenter Model Commands
  • 6.
    Moxy – те ория View ViewState Presenter Model Commands
  • 7.
    Moxy – те ория View ViewState Presenter Model Commands
  • 8.
    Moxy – те ория ViewState Presenter Model Commands View
  • 9.
    Moxy – те ория ViewState Presenter Model Commands View
  • 10.
    Moxy – те ория ViewState Presenter Model Commands View
  • 11.
    Moxy – те ория ViewState Presenter Model Commands View View
  • 12.
    Moxy – инст руме нт ы
  • 13.
    Moxy – MvpPre s e nt e r • -Содержит в себе часть бизнес логики • -Отвечает только за одну логическую единицу бизнес логики • Типизирован MvpView • :Имеет полезные методы • voidattachView(View view) и voiddetachView(View view) • View getViewState() • voidonFirstViewAttach() • boolean isInRestoreState(View view)
  • 14.
    Moxy – MvpVie w и MvpVi e wSt a t e • MvpView ,описывает команды которые Presenter может передать во View • MvpViewState ,хранит команды которые были переданы во View • MvpViewStateимеет метод voidrestoreState(View view) • Чаще всего не придётся самостояетльно создавать MvpViewState
  • 15.
    Moxy – @Inj e c t Vi e wSt a t e • Применяется к классу MvpPresenter • :Имеет три поведения • Если указан параметр value, то будет использован указанный ViewState • Если указан параметр view, то будет сгенерирован ViewState для указанной View • ,Если никакой параметр не указан то будет сгенерирован ViewState для View, которой типизирован Presenter(опасайтесь типизированных View) • Позволяет работать с методом View getViewState()
  • 16.
    Moxy – Sta t e St r a t e gy • Управляет нахождением команды во ViewState • Имеет два callback- :метод • voidbeforeApply(currentState, incomingState) • voidafterApply(currentState, incomingState) • Указать стратегию можно аннотацией @StateStrategyType • @StateStrategyType :можно применить • Ко всему интерфейсу View • К конкретному методу интерфейса View
  • 17.
    Moxy – MvpDel e ga t e • Управляет жизненным циклом Presenter • Подставляет во View правильный экземпляр Presenter • Отвечает за привязку View к Presenter • Имеет несколько методов для получения состяния View: • voidonCreate(Bundlebundle), voidonCreate() и voidonDestroy() • voidonStart() • voidonSaveInstanceState(BundleoutState) • voidsetParentDelegate(MvpDelegatedelegate, StringchildId)
  • 18.
    Moxy – @Inj e c t Pr e s e nt e r • Применяется к Presenter-полям реализации View • Сообщает MvpDelegate, какой Presenter нужно использовать • Может быть двух(с половиной) :типов • PresenterType.LOCAL • PresenterType.GLOBAL и PresenterType.WEAK • , !=В случае если тип PresenterType.LOCAL, MvpDelegateбудет искать Presenter :по одному из правил • ,По статичному тэгу указанному в параметре tag • Используя factory, сгенерирует тэг и сам Presenter, по необходимости
  • 19.
    Moxy – Model :Содержит в себе работу с данными • Получение данных • Из хранилища • Из интернате • Хранение данных • Обработка данных ,Самое подходящее место чтобы разгуляться DI
  • 20.
    Moxy – пример #1 :Задача сделать экран авторизации • :По нажатию на кнопку входа • Показать диалог запроса • Начать асинхронный запрос авторизации • :После завершения асинхронного запроса авторизации • Скрыть диалог прогресса • ,Если пришла ошибка то показать диалог с ошибкой • ,Если авторизация прошла успешно то перейти на главный экран
  • 21.
    Moxy – пример #1 :Задача сделать экран авторизации :Решение •Сделать SignInView •Сделать SignInPresenter •Сделать SignInActivity
  • 22.
    Moxy – пример #1 @StateStrategyType(AddToEndSingleStrategy.class) public interface SignInView extends MvpView { void toggleProgress(boolean show); void showError(Exception exception); void hideError(); void onSignIn(); } public class SignInActivity extends MvpActivity implements SignInView { @InjectPresenter SignInPresenter mSignInPresenter; ...
  • 23.
    Moxy – пример #1 @InjectViewState public class SignInPresenter extends MvpPresenter<SignInView> { @Inject Repository mRepository; public SignInPresenter() { WagamamaApplication.getAppComponent().inject(this); } public void auth(final String login, final String password) { getViewState().hideError(); getViewState().toggleProgress(true); ↓↓↓ ↓↓↓ mRepository.authentication().signIn(login, password) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<AuthenticateData>() { @Override public void onCompleted() { getViewState().toggleProgress(false); getViewState().onSignIn(); } @Override public void onError(Throwable e) { getViewState().toggleProgress(false); getViewState().showError(new Exception(e)); } });
  • 24.
    Moxy – пример #2 :Задача сделать аудиоплеер •В приложении есть список треков • Текущий трек выделяется и имеет кнопку play/ pause • ,В приложении есть фрагмент в котором отображается ,текущий трек его состяние и кнопки play/ pauseи prev/ next •Приложение отображает notification с отображением текущего ,трека его состяние и кнопок play/ pauseи prev/ next
  • 25.
    Moxy – пример #2 :Задача сделать аудиоплеер :Решение •Сделать PlayerView •Сделать PlayerPresenter •Сделать PlaylistAdapter •Сделать PlayerFragment •Сделать PlayerService
  • 26.
    Moxy – пример #2 public interface PlayerView extends MvpView { String PLAYER_STATE = "playerState"; @StateStrategyType(SingleStateStrategy.class) void setCurrentTrack(TrackInfo track); @StateStrategyType(value = PlayerStateStrategy.class, tag = PLAYER_STATE) void playTrack(); @StateStrategyType(value = PlayerStateStrategy.class, tag = PLAYER_STATE) void pauseTrack(); }
  • 27.
    Moxy – пример #2 public class PlayerStateStrategy implements StateStrategy { @Override public <View extends MvpView> void beforeApply(List<Pair<ViewCommand<View>, Object>> currentState, Pair<ViewCommand<View>, Object> incomingState) { for (Pair<ViewCommand<View>, Object> viewCommand : currentState) { if (viewCommand.first.getTag().equals(PlayerView.PLAYER_STATE)) { currentState.remove(viewCommand); break; } } currentState.add(incomingState); } @Override public <View extends MvpView> void afterApply(List<Pair<ViewCommand<View>, Object>> currentState, Pair<ViewCommand<View>, Object> incomingState) { } }
  • 28.
    Moxy – пример #2 public class PlayerPresenter extends MvpPresenter<PlayerView> { public static final String TAG = "player"; public PlayerPresenter() { super(); PlayerApp.get().getBus().register(this); } @Subscribe public void playTrack(PlayTrackEvent event) { playTrack(event.getTrack()); } public void playTrack(TrackInfo trackInfo) { getViewState().setCurrentTrack(trackInfo); getViewState().playTrack(); } ↓↓↓ ↓↓↓ public void playTrack() { getViewState().playTrack(); } public void pauseTrack() { getViewState().pauseTrack(); } @Override public void onDestroy() { super.onDestroy(); PlayerApp.get().getBus().unregister(this); } }
  • 29.
    Moxy – пример #2 public class PlaylistAdapter extends BaseAdapter implements PlayerView { @InjectPresenter(type = PresenterType.WEAK, tag = PlayerPresenter.TAG) PlayerPresenter mPlayerPresenter; public PlaylistAdapter(MvpDelegate<?> parentDelegate) { MvpDelegate<PlaylistAdapter> delegate = new MvpDelegate<>(this); delegate.setParentDelegate(parentDelegate, ""); delegate.onCreate(); } ... public class MainActivity extends MvpActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); PlaylistAdapter adapter = new PlaylistAdapter(getMvpDelegate()); ...
  • 30.
    Moxy – пример #2 public class PlayerService extends Service implements PlayerView { @InjectPresenter(type = PresenterType.WEAK, tag = PlayerPresenter.TAG) PlayerPresenter mPlayerPresenter; private MvpDelegate<PlayerService> mDelegate; @Override public void onCreate() { super.onCreate(); mDelegate = new MvpDelegate<>(this); mDelegate.onCreate(null); mDelegate.onStart(); } ... @Override public void onDestroy() { super.onDestroy(); mDelegate.onDestroy(); ...
  • 31.
    Moxy – пример #2 public class PlayerFragment extends MvpFragment implements PlayerView { @InjectPresenter(type = PresenterType.WEAK, tag = PlayerPresenter.TAG) PlayerPresenter mPlayerPresenter; ...
  • 32.
    Moxy – конкурент ы главный конкурент– одинMosby с неудобным ViewState
  • 33.
    Moxy – итог о • :Что имеем • Решены проблемы с жизненным циклом • -Всегда отображается актуальное состояние бизнес логики • Codegeneration • :Важные советы • Не меняйте View командой из View • Добавляйте и удаляйте элементы View только через Presenter • ,Если не компилится внимательно вчитайтесь в описание ошибки • Используйте DI для связки Presenter↔Model • MVP != инструмент
  • 34.
    На почит ат ь • Moxy — MVP Androidреализация под с щепоткой магии • AndroidApplication Architecture(AndroidDev Summit 2015) • AndroidTestingCodelab • Nucleus • Mosby • OldMosby • STINSON'SPLAYBOOK FORMOSBY  • AndroidReactiveMVP: практика • AndrtoidClean Architecture  • . Speaker Clean Architecture MVPАлексей Макаров и   • Mosby issues 85