Источник: Nuances of Programming
Пагинация — достаточно известная, но трудная в реализации функция. Поэтому я решил разработать демонстрационное приложение на основе Android Paging Library с пагинацией, основанной на пользовательских запросах в поиске. Я также использовал Retrofit и следующие библиотеки:
- Kotlin Coroutines: Для асинхронного программирования.
- Koin: Для внедрения зависимости.
Как работает это приложение?
Разобраться в приложении достаточно просто. Значок “Search” находится на панели инструментов. Нажав на него, пользователь начинает печатать необходимый запрос: при каждом совпадении буквы обновляется RecyclerView, а на API Github запускается новый запрос (если запущен предыдущий, то он отклоняется).
Круговой индикатор процесса находится в нижней части RecyclerView и отображает загрузку следующего запроса.
Пользователь также может использовать фильтр запросов с помощью диалога после нажатия FAB.
Я также использовал обработчик ошибок сети. Таким образом, можно уведомить пользователя о случившейся ошибке. В этом приложении происходят два типа ошибок:
- Pagination error: первый запрос выполнен правильно, а второй дает сбой… В этом случае пользователь получает сообщение и кнопку “повторить запрос”.
- Global error: первый запрос дает сбой… В этом случае выходит сообщение и кнопка “обновить” вместо RecyclerView.
Как создать такое приложение?
Процесс создания относительно прост.
Изначально у нас есть фрагмент кода, содержащий RecyclerView, связанный с PagedList с помощью LiveData. PagedList — это список (List), который загружает данные по фрагментам (страницам) из DataSource, созданным с помощью DataSource.Factory.
В данном примере UserDataSource не выдает данные напрямую. Эту функцию выполняет UserRepository.
Покажите мне код!
Retrofit & Coroutines
При использовании coroutines с Retrofit каждый вызов должен возвращать ответ Deferred.
Затем в UserRepository нужно вызвать каждый предыдущий запрос с помощью функций suspend с использованием метода .await():
Функции suspend достаточно читабельны (даже для тех, кто не знаком с coroutines), поэтому легко отгадать, что именно получит с API Github каждая из них.
Paging Library
После подготовки сетевых запросов переходим к настройке классов из Paging Library. Сначала создаем UserDataSource:
Этот класс представляет собой сердце пагинации и наследуется из PageKeyedDataSource.
Метод executeQuery() загружает новый coroutine, который получит данные с API Github. Я также использовал SupervisorJob для облегчения обработки возможных сбоев и отмены действий дочерних элементов.
CoroutineExceptionHandler управляет не перехваченными исключениями (uncaught exceptions).
Затем создаем UserDataSource с помощью UserDataSourceFactory, который наследует DataSource.Factory:
Можно заметить, что объекты CoroutineScope и UserRepository дважды переданы в оба конструктора UserDataSourceи UserDataSourceFactory.
Изначально эти объекты были созданы с помощью SearchUserViewModel. Таким образом, при уничтожении VM, можно с легкостью прекратить работу запущенных coroutines.
ViewModel
SearchUserViewModel сконструирует все предыдущие объекты, чтобы создать LiveData из PagedList:
Для наглядности рассмотрим пример с классом BaseViewModel:
Koin
Для внедрения зависимости былиспользован Koin из-за его читаемости и простоты использования.
Рассмотрим пример сетевого модуля Koin для этого приложения:
Выглядит неубедительно? Рассмотрим модуль ViewModel:
val viewModelModule = module {
viewModel { SearchUserViewModel(get(), get()) }
}
Все максимально понятно 👌
Тестирование
Unit Tests
Благодаря Koin и Coroutines модульное тестирование становится более удобным и читабельным:
Для справки: я использовал MockWebServer для имитации HTTP-сервера и пользовательских ответов.
Instrumented Tests
Повторюсь, тестировать с Koin намного проще, особенно при обновлении зависимостей контроллера (Activity/Fragment) перед каждым тестом.
Возможно, вы заметили Thread.sleep(1000)? RecyclerView исчезает на некоторое время, когда никаких данных не загружено.
Мы рассмотрели использование Android Paging Library с Retrofit и Coroutines, а также несколько способов тестирования.
Весь код проекта доступен в этом репозитории.
Читайте нас в телеграмме и vk
Перевод статьи Philippe BOISNEY: Playing with…