O slideshow foi denunciado.
Utilizamos seu perfil e dados de atividades no LinkedIn para personalizar e exibir anúncios mais relevantes. Altere suas preferências de anúncios quando desejar.

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

10.010 visualizações

Publicada em

Доклад о подходе к разработке Android-приложений с использованием MVP и Clean Architecture. Никита рассмотрит преимущества этого подхода перед традиционным, уделит отдельное внимание вопросам сохранения состояния в Android-MVP, а также особенностям взаимодействия между V и P.

Publicada em: Software
  • Login to see the comments

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

  1. 1. Как я научился не волноваться и полюбил Android-MVP Никита Баришок
  2. 2. План Почему MVP? Android-MVP: главное Android-MVP: реальность О взаимодействии между V и P Итоги 2
  3. 3. Почему MVP? 3
  4. 4. Почему MVP? Важные характеристики системы: -  понятность кода -  расширяемость -  готовность к изменениям -  тестируемость 4
  5. 5. Почему MVP? Важные характеристики системы: -  понятность кода -  расширяемость -  готовность к изменениям -  тестируемость Это всё НЕ о традиционном подходе! 5
  6. 6. Почему 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
  7. 7. Почему 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
  8. 8. Android-MVP: теория 8
  9. 9. Android-MVP: описание MVP Смысл: отделить представление от логики Для Android: помогает решить проблему, когда Activity выступает в роли God Object 9
  10. 10. Android-MVP: описание MVP VIEW PRESENTER MODEL оповестить о событии запросить данные обновить UI получить данные View максимально прост и пассивен Model включает в себя слой получения данных и бизнес-логики Presenter получает данные из Model, трансформирует их, отдает во View; решает, что делает View. 10
  11. 11. Android-MVP: MVP + Clean architecture VIEW PRESENTER INTERACTOR REPOSITORY ENTITY1 ENTITY2 VIEWMODEL V P M DOMAIN LAYER DATA LAYER 11
  12. 12. Android-MVP: практика 12
  13. 13. Android-MVP: model public interface Api { @GET("weather") Observable<WeatherResponse> get(@Query("APPID") String key, @Query("q") String q); } REPOSITORY ENTITY1 ENTITY2 13
  14. 14. 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
  15. 15. 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
  16. 16. Android-MVP: model public interface WeatherRepository { Observable<WeatherResponse> getWeather(String city); } 16 REPOSITORY ENTITY1 ENTITY2
  17. 17. 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
  18. 18. 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
  19. 19. 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
  20. 20. 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
  21. 21. 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
  22. 22. 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
  23. 23. Android-MVP: view public class WeatherFragment extends Fragment implements WeatherView { @Override public void showLoading() { // показать индикатор загрузки } ... } 23 VIEW
  24. 24. 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
  25. 25. 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
  26. 26. Android-MVP: реальность 26
  27. 27. 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
  28. 28. Android-MVP: реальность (2) 28
  29. 29. Android-MVP: реальность (2) 29
  30. 30. Android-MVP: реальность (2) 30
  31. 31. Android-MVP: реальность (2) 31
  32. 32. 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
  33. 33. Слой между View и Presenter: -  (Со стороны Presenter) это View без жизненного цикла (~POJO) -  (Со стороны View) это Presenter с возможностью восстановить состояние у View при необходимости Android-MVP: о взаимодействии V и P 33
  34. 34. 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
  35. 35. 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
  36. 36. 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
  37. 37. 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. 38. 38
  39. 39. 1 39
  40. 40. 2 40
  41. 41. 3 41
  42. 42. 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. 43. 43
  44. 44. 1 44
  45. 45. 2 45
  46. 46. 1 46
  47. 47. Итоги Android-MVP: -  Много кода +  Код намного более понятный и гибкий +  Качество приложения выше +  Скорость разработки выше +  UX лучше (если не теряется состояние) 47
  48. 48. Ресурсы 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
  49. 49. Самое время для вопросов Контакты: nikita_b@abbyy.com https://twitter.com/onemanparty_ 49

×