moxy – реализация mvp под android. С щепоткой магии

Post on 20-Mar-2017

475 Views

Category:

Software

11 Downloads

Preview:

Click to see full reader

TRANSCRIPT

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

Шмаков Юрийsenneco@gmail.com

Что такое MVPОсновные составляющие: Model, View и Presenter• Плюсы:• Код разбивается на мелкие, независимые кусочки• Сильно упрощается написание тестов к коду• Легко менять какую-то часть, не ломая при этом другую

•Минусы:• Кода становится больше• К этому подходу нужно привыкать• На данный момент не сильно распространённый

MVP  в AndroidПозволяет снять метку God Object с 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 – MvpPresenter• Содержит в себе часть бизнес-логики• Отвечает только за одну логическую единицу

бизнес-логики• Типизирован MvpView• Имеет полезные методы:• void attachView(View view) и void detachView(View view)• View getViewState()• void onFirstViewAttach()• boolean isInRestoreState(View view)

Moxy – MvpView и MvpViewState• MvpView описывает команды, которые Presenter

может передать во View• MvpViewState хранит команды, которые были

переданы во View• MvpViewState имеет метод void restoreState(View

view)• Чаще всего не придётся самостояетльно создавать

MvpViewState

Moxy – @InjectViewState• Применяется к классу MvpPresenter• Имеет три поведения:• Если указан параметр value, то будет использован

указанный ViewState• Если указан параметр view, то будет сгенерирован

ViewState для указанной View• Если никакой параметр не указан, то будет сгенерирован

ViewState для View, которой типизирован Presenter(опасайтесь типизированных View)

• Позволяет работать с методом View getViewState()

Moxy – StateStrategy• Управляет нахождением команды во ViewState• Имеет два callback-метод:• void beforeApply(currentState, incomingState)• void afterApply(currentState, incomingState)

• Указать стратегию можно аннотацией @StateStrategyType• @StateStrategyType можно применить:• Ко всему интерфейсу View• К конкретному методу интерфейса View

Moxy – MvpDelegate• Управляет жизненным циклом Presenter• Подставляет во View правильный экземпляр Presenter• Отвечает за привязку View к Presenter• Имеет несколько методов для получения состяния

View:• void onCreate(Bundle bundle), void onCreate() и void

onDestroy()• void onStart()• void onSaveInstanceState(Bundle outState)• void setParentDelegate(MvpDelegate delegate, String childId)

Moxy – @InjectPresenter• Применяется к Presenter-полям реализации View• Сообщает MvpDelegate, какой Presenter нужно

использовать• Может быть двух(с половиной) типов:• PresenterType.LOCAL• PresenterType.GLOBAL и PresenterType.WEAK

• В случае, если тип != PresenterType.LOCAL, MvpDelegate будет искать Presenter по одному из правил:• По статичному тэгу, указанному в параметре tag• Используя factory, сгенерирует тэг и сам Presenter, по

необходимости

Moxy – ModelСодержит в себе работу с данными:• Получение данных

• Из хранилища• Из интернате

• Хранение данных• Обработка данных

Самое подходящее место, чтобы разгуляться 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@InjectViewStatepublic 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 – пример #2public 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 – пример #2public 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 – пример #2public 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 – пример #2public 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 – пример #2public 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 – пример #2public class PlayerFragment extends MvpFragment implements PlayerView{ @InjectPresenter(type = PresenterType.WEAK, tag = PlayerPresenter.TAG) PlayerPresenter mPlayerPresenter; ...

Moxy – конкурентыглавный конкурент– одинMosby с неудобным ViewState

Moxy – итого• Что имеем:

• Решены проблемы с жизненным циклом• Всегда отображается актуальное состояние бизнес-логики• Code generation

• Важные советы:• Не меняйте View командой из View • Добавляйте и удаляйте элементы View только через Presenter• Если не компилится, внимательно вчитайтесь в описание

ошибки• Используйте DI для связки Presenter↔Model• MVP != инструмент

На почитать• Moxy — реализация MVP под Android с щепоткой магии• Android Application Architecture (Android Dev Summit 2015)• Android Testing Codelab• Nucleus• Mosby• Old Mosby• STINSON'S PLAYBOOK FOR MOSBY • Android Reactive MVP: практика• Andrtoid Clean Architecture • Алексей Макаров. Speaker Clean Architecture и MVP • Mosby issues 85 

top related