«Как я научился не волноваться и полюбил android-mvp»,...

49
Как я научился не волноваться и полюбил Android-MVP Никита Баришок

Upload: mailru-group

Post on 20-Mar-2017

7.602 views

Category:

Software


5 download

TRANSCRIPT

Как я научился не волноваться и полюбил Android-MVP

Никита Баришок

План

Почему MVP?

Android-MVP: главное

Android-MVP: реальность

О взаимодействии между V и P

Итоги

2

Почему MVP?

3

Почему MVP?

Важные характеристики системы:

-  понятность кода

-  расширяемость

-  готовность к изменениям

-  тестируемость

4

Почему MVP?

Важные характеристики системы:

-  понятность кода

-  расширяемость

-  готовность к изменениям

-  тестируемость

Это всё НЕ о традиционном подходе!

5

Почему MVP?

MVP / MVVM(С) / ... фундаментально об одном и том же:

Добиваться повышения качества системы за счет разбиения её на слои с четко выраженными обязанностями и абстрагирования этих слоев друг от друга.

/* If you put ten software architects into a room and have them discuss what the Model-View-Controller pattern is, you will end up with twelve different opinions.(с) */

6

Почему MVP?

MVP / MVVM(C) / ... фундаментально об одном и том же:

Добиваться повышения качества системы за счет разбиения её на слои с четко выраженными обязанностями и абстрагирования этих слоев друг от друга.

MVP:

+  легче поддается тестированию

-  требует больше кода

/* If you put ten software architects into a room and have them discuss what the Model-View-Controller pattern is, you will end up with twelve different opinions.(с) */

7

Android-MVP: теория

8

Android-MVP: описание MVP

Смысл:

отделить представление от логики

Для Android:

помогает решить проблему, когда Activity выступает в роли God Object

9

Android-MVP: описание MVP

VIEW PRESENTER MODEL

оповестить о событии

запросить данные

обновить UI получить данные

View максимально прост и пассивен Model включает в себя слой получения данных и бизнес-логики Presenter получает данные из Model, трансформирует их, отдает во View; решает, что делает View.

10

Android-MVP: MVP + Clean architecture

VIEW PRESENTER INTERACTOR REPOSITORY

ENTITY1

ENTITY2

VIEWMODEL

V P M

DOMAIN LAYER DATA LAYER

11

Android-MVP: практика

12

Android-MVP: model public interface Api { @GET("weather") Observable<WeatherResponse> get(@Query("APPID") String key, @Query("q") String q); }

REPOSITORY

ENTITY1

ENTITY2

13

Android-MVP: model public interface Api { @GET("weather") Observable<WeatherResponse> get(@Query("APPID") String key, @Query("q") String q); } public class WeatherResponse { Coord coord; List<Weather> weather; String base; Main main; Wind wind; Clouds clouds; double dt; Sys sys; int id; String name; int cod; }

14

REPOSITORY

ENTITY1

ENTITY2

Android-MVP: model public interface Api { @GET("weather") Observable<WeatherResponse> get(@Query("APPID") String key, @Query("q") String q); } public class WeatherResponse { Coord coord; List<Weather> weather; String base; Main main; Wind wind; Clouds clouds; double dt; Sys sys; int id; String name; int cod; }

public class Main { double temp; double pressure; double humidity; double tempMin; double tempMax; }

15

public class Wind { double speed; double deg; } ...

REPOSITORY

ENTITY1

ENTITY2

Android-MVP: model public interface WeatherRepository { Observable<WeatherResponse> getWeather(String city); }

16

REPOSITORY

ENTITY1

ENTITY2

Android-MVP: model public interface WeatherRepository { Observable<WeatherResponse> getWeather(String city); } public class WeatherRetrofitRepository implements WeatherRepository { Api api; public WeatherRetrofitRepository(Api api) { this.api = api; } @Override public Observable<WeatherResponse> getWeather(String city) { return api.get(BuildConfig.WEATHER_API_KEY, city); } }

17

REPOSITORY

ENTITY1

ENTITY2

Android-MVP: model public interface GetWeatherInMoscowInteractor { Observable<WeatherResponse> get(); } public class GetWeatherInMoscowUseCase implements GetWeatherInMoscowInteractor { private final WeatherRepository repository; public GetWeatherInMoscowUseCase(WeatherRepository repo) { repository = repo; } @Override public Observable<WeatherResponse> get() { return repository.getWeather("Moscow"); } }

INTERACTOR

18

Android-MVP. Практика: Model public interface GetWeatherInMoscowInteractor { Observable<WeatherResponse> get(); } public class GetWeatherInMoscowUseCase implements GetWeatherInMoscowInteractor { private final WeatherRepository repository; private final CacheManager cacheManager; public GetWeatherInMoscowUseCase(WeatherRepository repo, CacheManager cacheMan) { repository = repo; cacheManager = cacheMan; } @Override public Observable<WeatherResponse> get() { return repository .getWeather("Moscow") .doOnNext(weather -> {cacheManager.put(weather);}); } }

19 INTERACTOR

Android-MVP: presenter public abstract class Presenter<V> { private volatile V view; public void attachView(V v) { view = v; } public void detachView() { view = null; } public void onCreate(Bundle arguments, Bundle savedInstanceState) { } public void onSaveInstanceState(Bundle bundle) { } public void onDestroy() { } }

PRESENTER

VIEWMODEL

20

public class WeatherPresenterImpl extends WeatherPresenter { private GetWeatherInMoscowInteractor getWeather; private WeatherMapper mapper; @Override public void loadWeather() { getWeather.get() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(weather -> updateUi(mapper.map(weather)),

throwable -> showError(WeatherError.GENERAL))); } } public abstract class WeatherPresenter extends Presenter<WeatherView> { public abstract void loadWeather(); }

Android-MVP: presenter

21

PRESENTER

VIEWMODEL

Android-MVP: view public interface WeatherView extends LCEView<WeatherViewModel,WeatherError> { enum WeatherError { GENERAL } } public interface LCEView<D, E> { void showLoading(); void hideLoading(); void setData(D data); void showContent(); void showError(E error); } public class WeatherViewModel implements Parcelable { int temperature; }

VIEW

22

Android-MVP: view public class WeatherFragment extends Fragment implements WeatherView { @Override public void showLoading() { // показать индикатор загрузки } ... }

23

VIEW

Android-MVP: view public class WeatherFragment extends Fragment implements WeatherView { WeatherPresenter presenter = new WeatherPresenterImpl(new GetWeatherInMoscowUseCase( new WeatherRetrofitRepository(RetrofitHelper.getWeatherApi())), new WeatherMapperImpl()); @Override public void showLoading() { // показать индикатор загрузки } ... }

24

VIEW

Android-MVP: view public class WeatherFragment

extends ComponentManagerFragment<WeatherComponent, WeatherView> implements WeatherView {

@Override protected WeatherComponent createComponent() { return DaggerWeatherComponent .builder() .appComponent(...) .build(); } @Override public void onViewCreated(final View view, final Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); getPresenter().loadWeather(); }

... }

25

VIEW

Android-MVP: реальность

26

Android-MVP: реальность public class WeatherFragment

extends ComponentManagerFragment<WeatherComponent, WeatherView> implements WeatherView {

... @Override public void onViewCreated(final View view, final Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); getPresenter().loadWeather(); } ... }

27

Android-MVP: реальность (2)

28

Android-MVP: реальность (2)

29

Android-MVP: реальность (2)

30

Android-MVP: реальность (2)

31

public class WeatherPresenterImpl extends WeatherPresenter { @Override public void loadWeather() { getWeather.get() .subscribe(weather -> {

if (view != null) { updateUi(mMapper.map(weather)); } }, throwable -> { if (view != null) { showError(WeatherView.WeatherError.GENERAL); } }));

} }

Android-MVP: реальность (2)

32

Слой между View и Presenter: -  (Со стороны Presenter) это View без жизненного цикла (~POJO)

-  (Со стороны View) это Presenter с возможностью восстановить состояние у

View при необходимости

Android-MVP: о взаимодействии V и P

33

public class WeatherPresenterImpl extends WeatherPresenter { } public class WeatherFragment implements WeatherView { } public class WeatherCommunicationBus extends WeatherPresenter implements WeatherView { // view @Override public void showLoading() { if (view != null) { view.showLoading(); } } ... // presenter @Override public void loadWeather() { presenter.loadWeather(); } ... }

Android-MVP: о взаимодействии V и P

34

public class WeatherCommunicationBus extends WeatherPresenter implements WeatherView { public WeatherCommunicationBus(WeatherPresenter presenter) { mPresenter = presenter; mPresenter.attachView(this); } ... @Override public void onDestroy() { mPresenter.detachView(); mPresenter.onDestroy(); } ... }

Android-MVP: о взаимодействии V и P

35

public class WeatherViewState implements ViewState<WeatherView>, Parcelable { private final static int STATE_UNINITIALIZED = -1; private final static int STATE_DEFAULT = 0; private final static int STATE_SHOW_LOADING = 1; private final static int STATE_SHOW_ERROR = 2; private int mCurrentState = 0; private WeatherView.WeatherError mError; private WeatherViewModel mModel; public void setStateShowLoading() { mCurrentState = STATE_SHOW_LOADING; } ... public void apply(WeatherView view) { switch (mCurrentState) { case STATE_SHOW_LOADING: view.showLoading(); break; ... }

Android-MVP: о взаимодействии V и P

36

public abstract class MvpLceViewStateFragment extends MvpLceFragment { ... @Override public void showContent() { super.showContent(); viewState.setStateShowContent(getData()); } @Override public void showError(Throwable e, boolean pullToRefresh) { super.showError(e, pullToRefresh); viewState.setStateShowError(e, pullToRefresh); } ... }

Android-MVP: о взаимодействии V и P

37

38

1

39

2

40

3 41

public class WeatherCommunicationBus extends WeatherPresenter implements WeatherView { private final WeatherPresenter presenter; private WeatherView view; private WeatherViewState viewState; public WeatherCommunicationBus(WeatherPresenter presenter) { presenter = presenter; viewState = new WeatherViewState(); presenter.attachView(this); } @Override public void showLoading() { viewState.setStateShowLoading(); if (view != null) { mView.showLoading(); } } @Override public void attachView(WeatherView view) { view = view; viewState.apply(view); } }

Android-MVP: о взаимодействии V и P

42

43

1

44

2

45

1

46

Итоги Android-MVP:

-  Много кода

+  Код намного более понятный и гибкий

+  Качество приложения выше

+  Скорость разработки выше

+  UX лучше (если не теряется состояние)

47

Ресурсы GIT:

https://github.com/nbarishok/RxMvpAndroid

Medium: https://medium.com/@nbarishok/on-communication-between-v-and-p-in-android-mvp-16caf773e1a5#.6mhrjpkw4

Dagger 2 + custom scopes:

https://guides.codepath.com/android/Dependency-Injection-with-Dagger-2

http://frogermcs.github.io/dependency-injection-with-dagger-2-custom-scopes/

DI & Сохранение presenter’a:

http://blog.bradcampbell.nz/mvp-presenters-that-survive-configuration-changes-part-2/ 48

Самое время для вопросов

Контакты:

[email protected]

https://twitter.com/onemanparty_

49