Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Clean Architecture for Android

uaMobiTech
November 14, 2015

Clean Architecture for Android

By Alexander Bobro - https://www.facebook.com/profile.php?id=100002522756075&fref=ts
Why do mobile applications need architecture? We will discuss different architectural approaches for building flexible and testable software. I will show why clean architecture is one the best solutions. In this context, we will discuss different ways to organize presentation layer of Android apps, reveal pros and cons of them. I will show how to implement MVP for your android application, what problems you may face and how to solve them, how to test resulting solution.

uaMobiTech

November 14, 2015
Tweet

More Decks by uaMobiTech

Other Decks in Programming

Transcript

  1. The Clean Architecture выбор структурных элементов и их интерфейсов, с

    помощью которых составлена система, а также их поведения в рамках сотрудничества структурных элементов; соединение выбранных элементов структуры и поведения во всё более крупные системы; архитектурный стиль, который направляет всю организацию — все элементы, их интерфейсы, их сотрудничество и их соединение. Архитектура программного обеспечения (англ. software architecture) — совокупность важнейших решений об организации программной системы. Архитектура включает:
  2. The Clean Architecture • Сделать возможным/Ускорить/Удешевить достижение бизнес-цели. • Сделать

    приложение легко расширяемым на случай, если это понадобится • Уменьшить количество ошибок. • Сделать код более понятным и читабельным • Увеличить тестируемость кода. • Облегчить поддержку проекта или его отдельных модулей (в том числе и при смене разработчиков) Зачем нужна архитектура?
  3. Архитектурные шаблоны программного обеспечения (англ. Software architectural patterns) - обобщенные,

    переиспользуемые решения для часто встречающихся проблем в архитектуре программного обеспечения при определенном контексте. Архитектурные паттерны имеют более широкий масштаб относительно паттернов проектирования (design patterns).
  4. © 2015 Cogniance Inc. All Rights Reserved. Proprietary and Confidential.

    solution The Clean Architecture Требования к архитектуре • предсказуемость, понятность • тестируемость • независимость от библиотек и фреймверков • независимость от UI • независимость от базы данных • независимость от внешних источников данных
  5. Implementation layers PRESENTATION LAYER Model View Presenter DOMAIN LAYER Business

    Objects Business Logic Interactor Implementations DATA LAYER Repository Implementations API Client Interfaces DAO Interfaces INTERACTOR INTERFACES REPOSITORY INTERFACES CLOUD DB MEMORY
  6. The Clean Architecture DOMAIN LAYER Business Objects Business Logic Interactor

    Implementations INTERACTOR INTERFACES REPOSITORY INTERFACES
  7. Collection<T> read(Criteria criteria) throws DataSourceException ; Collection<T> write(Iterable<T> data) throws

    DataSourceException ; Collection<T> delete(Criteria criteria) throws DataSourceException ; Collection<T> deleteAll(Iterable<T> data) throws DataSourceException ; Repository<T>
  8. protected abstract Observable<T> provideUseCaseObservable(P params); public void execute(Subscriber<T> useCaseSubscriber, P

    params) { this.subscription = this.provideUseCaseObservable(params) .subscribeOn(subscribeOnScheduler) .observeOn(observeOnScheduler) .subscribe(useCaseSubscriber); } public void unsubscribe() { if (!subscription.isUnsubscribed()) { subscription.unsubscribe(); } } UseCase<T,P>
  9. private final RepositoryBase<Post> postRepository ; @Override protected Observable<List<Post>> provideUseCaseObservable (String

    userId) { final Criteria criteria = new UserIdCriteria(userId) ; return Observable. create(new Observable.OnSubscribe<List<Post>>() { @Override public void call(Subscriber<? super List<Post>> subscriber) { try { subscriber.onNext(Stream. stream(postRepository .read(criteria)).toList()) ; } catch (DataSourceException e) { subscriber.onError(e) ; } finally { subscriber.onCompleted() ; } } }); } GetPostsByUserIdUseCase
  10. ParseClientReaderSdk<T, P extends ParseObject> Class<P> parseClass; ObjectConverter< T, P> objectConverter

    ; ExceptionConverter exceptionConverter ; @Override public Collection<T> get(Criteria criteria) throws DataLayerException { ParseQuery<P> parseQuery = ParseQueryFactory. fromCriteria(parseClass, criteria); try { List<P> parseObjects = parseQuery.find() ; return Stream.stream(parseObjects).map( objectConverter ::convert).toList() ; } catch (ParseException e) { throw exceptionConverter .convert(e); } }
  11. @Override public Collection<Post> read(Criteria criteria) throws DataSourceException { if(isMemoryCacheValid(criteria)) {

    return getDataFromMemoryCache(criteria) ; } else if(isDataBaseCacheValid(criteria)) { return getDataFromDataBaseCache(criteria) ; } else { return getDataFromServer(criteria) ; } } public Collection<Post> getDataFromServer (Criteria criteria) throws DataSourceException { try { Collection<Post> posts = postClient.get(criteria) ; return cachePosts(posts) ; } catch (RESTException e) { throw dataSourceExceptionConverter .convert(e) ; } } PostsRepository
  12. @Override public void loadData() { view.showLoading(); dataUseCase.execute(new DataSubscriber(), getParams()); }

    @Override public void onDataReceived(D data) { this.data = data; view.hideLoading(); updateViewState(); } protected void updateViewState() { view.displayData(domainToViewModelConverter.convert(data)); } DataPresenterBase
  13. Возникшие вопросы 1. что делать, если презентер "переживает" представление (опасность

    утечки представления)? 2. что делать, если представление не может выполнить вызов презентера (например находится в неподходящем состоянии)? 3. что делать с параметрами, переданными в активити/фрагмент через интент или бандл? 4. что делать с жизненным циклом компонент андроида? 5. как сохранять состояние вью и презентеров? 6. что делать с компонентами зависящими от Context, Activity, Fragment? 7. что делать с навигацией?
  14. @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); V viewBase

    = getViewBase(); P presenterBase = getPresenterBase(); initView(viewBase, getView(), savedInstanceState); initPresenter(presenterBase, savedInstanceState); viewBase.setPresenter(presenterBase); presenterBase.attachToView(viewBase); } @Override public void onDestroyView() { super.onDestroyView(); getViewBase().releaseViewComponents(); getPresenterBase().detachFromView(); } FragmentBase<V,P>
  15. public interface UserPostsScreenNavigation { void navigateToUserDetailsScreen(User user); void navigateToPostDetailsScreen(Post post);

    } UserPostsScreenNavigation navigation; @Override public void onUserSelected(User user) { navigation.navigateToUserDetailsScreen(user); } @Override public void onPostSelected(Post post) { navigation.navigateToPostDetailsScreen(post); } Navigation
  16. MVP. Расширенная схема PRESENTER NAVIGATOR VIEW APPLICATION MODEL state change

    events call methods initializes presenter makes call during lifecycle navigation events updated views user events call application methods to switch views
  17. The Clean Architecture PRESENTER NAVIGATOR VIEW APPLICATION INTERACTOR state change

    events call methods initializes presenter makes call during lifecycle navigation events updated views user events DATASOURCE ENTITIES manipulates entities state change events manipulates data call application methods to switch views