Архитектура построена на базе чёткого разделения проекта на уровни. В качестве вводных для данного подхода необходимо ознакомиться со следующими материалами:
- The Clean Architecture
- Architecting Android…The clean way?
- Architecting Android…The evolution
- Евгений Мацюк — Пишем тестируемый код
- Заблуждения Clean Architecture
- Activity/Fragment:
- логический компонент, привязанный к интерфейсу
- распологается в своём личном пакете
- выполняет функции приёма и вывода данных
- взаимодействует со своим Presenter и возможно вызывает другие Activity/Fragment
- Presenter:
- логический компонент, привязанный к интерфейсу
- распологается в своём личном пакете
- выполняет функции оркестрирования интерфейса и реализации её пведения (посредством вызовов методов View)
- взаимодействует со своим Interactor и своим View
- Interactor:
- логический компонент, привязанный к интерфейсу или типам сущности (есть и такие и такие)
- распологается в своём личном пакете
- выполняет функции реакции на данные с учётом логики программы (храняться основные бизнес правила)
- взаимодействует со своим Presenter и разными Repository
- Repository:
- логический компонент, привязанный к типам сущностей
- распологается в своём личном пакете
- выполняет функции приёма и вывода данных
- взаимодействует с вызывающим его Interactor'ом
- Activity/Fragment:
- создаётся Android (non-singletone, w/ scope)
- инициализируется в методах
View#onCreate()
(с завершением вFragment#onViewCreated()
дляFragment
) - Presenter внутри присваивается ч/з Dagger2
- инициализация Presenter внутри осуществляется в указанных методах (
View#onCreate()
,с завершением вFragment#onViewCreated()
дляFragment
) - Presenter:
- создаётся через Dagger2 (non-singletone, w/ scope)
- View присваивается самим View, ч/з
Presenter#bindView()
- инициализируется в методе
Presenter#initializePresenter()
, вызываемой View (потому что инициализацию нужно делать в подходящий момент, после инициализации View) - Interactor внутри присваивается/инициализируется ч/з Dagger2
- создание связи Interactor->Presenter выполняется в методе
Presenter#initializePresenter()
(ч/з другие методыInteractor
'а для Rx-инициализации) - Interactor:
- создаётся через Dagger2 (singletone, w/o scope)
- инициализируется через Dagger2 (
Interactor#initializeInteractor
) - Repository внутри присваивается/инициализируется ч/з Dagger2
- Repository:
- создаётся через Dagger2 (singletone, w/o scope)
- инициализируется через Dagger2 (
Repository#initializeRepository
)
- функционал одного направления лучше концентрировать в рамках одного
Interactor
(или вообще отдельного класса) - не использовть разные
Interactor
в разныхPresenter
(распределение может поменяться и знать об этомPresenter
вовсе не обязательно) - взаимодействие м/у
Interactor
'ами осуществлять внутриInteractor
'а, привязанного кPresenter
- на текущий момент используются 2 типа сущностей
data
/business+ui
- конвертация осуществляется в статических методах самих объектов
- основное правило конвертации: в более глубокий уровень (ближе к бизнесс логике) объекты должные передаваться уже сконвертированными
Основным средством передачи данных в системе являются Rx-потоки (на базе RxJava2) (см. http://reactivex.io, https://github.com/ReactiveX/RxJava). При этом в системе присутствуют следующие схемы взаимодействия через потоки: постоянные (без завершения) (например валидация форм или отметки о доступности сети/сервера) и завершаемые (передача данных списка/фактов). При этом используются следующие методы:
getXXXHotStream
– получить бесконечный hot stream для организации подписки (при этом подписка/отписка осуществляется вызывающей стороной)createXXXStream
– получить конечныйObservable
(при этом при полученииonComplete
возможна некоторая дополнительная обработка полученных данных)- при этом в случае если при получении данных используются только закэшированные данные, то используется метод
createXXXLocalStream
- при этом в случае если при получении данных используются только удалённые данные, то используется метод
createXXXSRemoteStream
- при этом в случае если при получении данных происходит кэширование успешно полученных данных, то используется метод
createXXXSRemoteCachedtream
Получение данных (в большинстве случаев) организуется таким образом:
- (уровень "data") формируется поток данных из БД (закэшированные данные)
- (уровень "data") формируется поток данных с сети (актуальные данные)
- (уровень "data") к потоку данных из сети подключается слушатель для обновления через транзакцию данных в БД (т.е. при успешном обновлении -- обновляется кэш)
- (уровень "data") итоговый поток объединяется таким образом, что при поступлении данных из сети (на текущий момент таймаут не проверяется) данные из кэша не берутся и наоборот
- уровень "ui" и "business' просто работают с данными - прозрачно пробрасывая исключения в "ui" (presenter)
При этом обновление данных оформляется аналогичным образом Хорошее пояснениние использованной методики (в технических деталях) тут: http://stackoverflow.com/a/36118469
Основными библиотека для тестирования являются
- бесконечная лента новостей просмотра новости (естественно….)
- ленты новостей от разных изданий
- категоризация каждой новости на отношений к одному или нескольким разделам (группировка по ключевым словам на сервере)
- ключевые тэги по каждой новости
- поиск новостей по тексту
- геолокация по указанному городу
- настройка источников по местоположению или ручная
- Местоположение: activity по-умолчанию
- Путь: activity по-умолчанию
- Источник данных: БД (кэш) и REST API (http://domain/articles)
- Особенности:
- первоначальная иницилизация интерфейсов и настроек пользователя
- скроллинг с выбором источников данных
- Местоположение: один из фрагментов в SourceBrowser's viewPager
- Путь: фрагмент по-умолчанию
- Источник данных: БД (кэш) и REST API (http://domain/articles)
- Особенности:
- Перелистывание влево и вправо позволяет переключаться на следующие источники
- Обновление свайпом
- Дозагрузка данных с использованием эмуляции бесконечного списка
- Возможны внедрения рекламы и viewPager вместо одного из элементов списка
- Местоположение: Переход из ленты при клике
- Путь: Лента -> клик по записи (контейнер записей)
- Источник данных:
- Особенности:
- Перелистывание влево и вправо позволяет переключаться на следующие/предыдущие новости в ленте текущего источника (source)
- Местоположение: Каждое перелистанная страница в браузере новостей
- Путь: Лента -> клик по записи (сама запись)
- Источник данных:
- Особенности:
- Пока только оотображение содержания новости
- Местоположение: SlidePaneLayout слева от ленты (по-умолчанию скрыта)
- Путь: Ленты новостей -> слиде с края экрана Источник данных: Пока предопределнные значения
- Особенности:
- В списке имеется поле для поиска, позволяющее осуществлять поиск новостей
- В списке на текущий момент предопределённые значения
- Клик на записях открывает соответствующие фрагменты или открывает ленту с фильтром по категории
- Местоположение: Выбор местоположения из настроек
- Путь: Ленты новостей -> слиде с края экрана -> выбор пункта местоположения
- Источник данных:
- Особенности:
- Отфильтрация на клиенте по мере ввода символов
- Сохранение в настройках приложения