Серия приложений Kriptofolio - Часть 5

В наши дни почти каждое приложение Android подключается к Интернету для получения / отправки данных. Вам обязательно нужно научиться работать с веб-службами RESTful, поскольку их правильная реализация является ключевым моментом при создании современных приложений.
Эта часть будет сложной. Мы собираемся объединить сразу несколько библиотек, чтобы получить рабочий результат. Я не собираюсь говорить о нативном Android способе обработки интернет-запросов, потому что в реальном мире им никто не пользуется. Каждое хорошее приложение не пытается изобретать велосипед, а вместо этого использует самые популярные сторонние библиотеки для решения типичных проблем. Было бы слишком сложно воссоздать функциональность, которую могут предложить эти хорошо сделанные библиотеки.
Содержание серии
- Введение: план создания современного Android-приложения на 2018–2019 годы
- Часть 1: Введение в принципы SOLID
- Часть 2: Как начать создавать приложение для Android: создание макетов, пользовательского интерфейса, XML-макетов
- Часть 3: Все об этой архитектуре: изучение различных архитектурных паттернов и способов их использования в вашем приложении
- Часть 4: Как реализовать инъекцию зависимостей в вашем приложении с помощью Dagger 2
- Часть 5. Обработка веб-служб RESTful с помощью Retrofit, OkHttp, Gson, Glide и Coroutines (вы здесь)
Что такое Retrofit, OkHttp и Gson?
Retrofit - это клиент REST для Java и Android. Эта библиотека, на мой взгляд, наиболее важна для изучения, поскольку она выполняет основную работу. Это позволяет относительно легко получать и загружать JSON (или другие структурированные данные) через веб-службу на основе REST.
В Retrofit вы настраиваете, какой конвертер будет использоваться для сериализации данных. Обычно для сериализации и десериализации объектов в JSON и из него вы используете библиотеку Java с открытым исходным кодом - Gson. Также, если вам нужно, вы можете добавить в Retrofit специальные преобразователи для обработки XML или других протоколов.
Для выполнения HTTP-запросов Retrofit использует библиотеку OkHttp. OkHttp - это чистый клиент HTTP / SPDY, отвечающий за любые низкоуровневые сетевые операции, кеширование, манипуляции с запросами и ответами. Напротив, Retrofit - это высокоуровневая абстракция REST, построенная на основе OkHttp. Модернизация тесно связана с OkHttp и интенсивно его использует.
Теперь, когда вы знаете, что все тесно связано, мы собираемся использовать все эти 3 библиотеки одновременно. Наша первая цель - получить весь список криптовалют с помощью Retrofit из Интернета. Мы будем использовать специальный класс перехватчика OkHttp для аутентификации CoinMarketCap API при обращении к серверу. Мы вернем результат данных JSON, а затем преобразуем его с помощью библиотеки Gson.
Быстрая установка Retrofit 2, чтобы сначала попробовать
Изучая что-то новое, я люблю опробовать это на практике, как только могу. Мы применим аналогичный подход к Retrofit 2, чтобы вы быстрее его поняли. Не беспокойтесь сейчас о качестве кода или каких-либо принципах программирования или оптимизации - мы просто напишем код, чтобы Retrofit 2 работал в нашем проекте, и обсудим, что он делает.
Выполните следующие действия, чтобы настроить Retrofit 2 в проекте приложения My Crypto Coins:
Сначала дайте приложению разрешение в ИНТЕРНЕТЕ
Мы собираемся выполнять HTTP-запросы на сервере, доступном через Интернет. Дайте это разрешение, добавив эти строки в свой файл манифеста:
Затем вам следует добавить зависимости библиотеки
Найдите последнюю Модернизированную версию. Также вы должны знать, что Retrofit не поставляется со встроенным конвертером JSON. Поскольку мы будем получать ответы в формате JSON, нам также необходимо вручную включить конвертер в зависимости. Мы собираемся использовать последний конвертер JSON от Google Gson version. Давайте добавим эти строки в ваш файл Gradle:
Как вы заметили из моего комментария, зависимость OkHttp уже поставляется с зависимостью Retrofit 2. Версии - это просто отдельный файл Gradle для удобства:
Затем настройте интерфейс модернизации
Это интерфейс, в котором объявляются наши запросы и их типы. Здесь мы определяем API на стороне клиента.
И настройте класс данных
Классы данных - это POJO (простые старые объекты Java), которые представляют ответы на вызовы API, которые мы собираемся сделать.
Создайте специальный класс перехватчика для аутентификации при вызове сервера
Это особый случай для любого API, который требует аутентификации для получения успешного ответа. Перехватчики - это мощный способ настроить ваши запросы. Мы собираемся перехватить фактический запрос и добавить отдельные заголовки запроса, которые будут подтверждать вызов с помощью API-ключа, предоставленного CoinMarketCap Professional API Developer Portal. Чтобы получить свой, вам необходимо зарегистрироваться там.
Наконец, добавьте этот код в нашу операцию, чтобы увидеть, как работает модернизация
Я хотел как можно скорее запачкать ваши руки, поэтому собрал все в одном месте. Это неправильный способ, но он самый быстрый, чтобы быстро увидеть визуальный результат.
Вы можете ознакомиться с кодом здесь. Помните, что это только начальная упрощенная версия реализации, чтобы вы могли лучше понять идею.
Окончательная правильная настройка для Retrofit 2 с OkHttp 3 и Gson
Хорошо, после небольшого эксперимента пришло время вывести эту реализацию модернизации на новый уровень. Мы уже получили данные успешно, но не правильно. Нам не хватает таких состояний, как загрузка, ошибка и успех. Наш код смешанный, без разделения проблем. Распространенная ошибка - писать весь код в действии или фрагменте. Наш класс активности основан на пользовательском интерфейсе и должен содержать только логику, которая обрабатывает взаимодействия пользовательского интерфейса и операционной системы.
На самом деле, после этой быстрой настройки я много поработал и внес много изменений. Нет смысла выкладывать весь код, который был изменен в статье. Лучше вместо этого просмотреть последний репозиторий кода части 5 здесь. Я все очень хорошо прокомментировал, и мой код должен быть понятен вам. Но я собираюсь рассказать о самых важных вещах, которые я сделал, и о том, почему я это сделал.
Первым шагом к улучшению было начало использования внедрения зависимостей. Помните, из предыдущей части мы уже правильно реализовали Dagger 2 внутри проекта. Поэтому я использовал его для настройки дооснащения.
Теперь, как вы видите, Retrofit отделен от класса активности, как и должно быть. Он будет инициализирован только один раз и использоваться для всего приложения.
Как вы могли заметить, при создании экземпляра конструктора Retrofit мы добавили специальный адаптер вызовов Retrofit с использованием addCallAdapterFactory. По умолчанию Retrofit возвращает Call<T>, но для нашего проекта мы требуем, чтобы он возвращал тип LiveData<T>. Для этого нам нужно добавить LiveDataCallAdapter, используя LiveDataCallAdapterFactory.
Теперь мы получим LiveData<T> вместо Call<T> в качестве типа возврата из методов обслуживания Retrofit, определенных в интерфейсе ApiService.
Еще один важный шаг - начать использовать шаблон репозитория. Об этом я уже говорил в Части 3. Ознакомьтесь с нашей схемой архитектуры MVVM из этого поста, чтобы запомнить, куда она идет.

Как вы видите на картинке, репозиторий - это отдельный слой для данных. Это наш единственный источник связи для получения или отправки данных. Когда мы используем репозиторий, мы следуем принципу разделения ответственности. У нас могут быть разные источники данных (например, в нашем случае постоянные данные из базы данных SQLite и данные из веб-сервисов), но репозиторий всегда будет единым источником истины для всех данных приложения.
Вместо того, чтобы напрямую связываться с нашей реализацией Retrofit, мы собираемся использовать для этого Repository. Для каждого типа сущностей у нас будет отдельный репозиторий.
Как вы заметили в коде класса CryptocurrencyRepository, я использую абстрактный класс NetworkBoundResource. Что это такое и зачем нам это нужно?
NetworkBoundResource - небольшой, но очень важный вспомогательный класс, который позволит нам поддерживать синхронизацию между локальной базой данных и веб-службой. Наша цель - создать современное приложение, которое будет бесперебойно работать, даже когда наше устройство отключено. Также с помощью этого класса мы сможем визуально представлять пользователю различные состояния сети, такие как ошибки или загрузка.
NetworkBoundResource начинает с наблюдения за базой данных ресурса. Когда запись загружается из базы данных в первый раз, она проверяет, достаточно ли хорош результат для отправки или его следует повторно получить из сети. Обратите внимание, что обе эти ситуации могут произойти одновременно, учитывая, что вы, вероятно, захотите отображать кэшированные данные при их обновлении из сети.
Если сетевой вызов завершается успешно, он сохраняет ответ в базе данных и повторно инициализирует поток. Если сетевой запрос не выполняется, NetworkBoundResource отправляет сообщение об ошибке напрямую.
Под капотом класс NetworkBoundResource создается с помощью MediatorLiveData и его способности одновременно наблюдать за несколькими источниками LiveData. Здесь у нас есть два источника LiveData: база данных и ответ на сетевой вызов. Оба эти LiveData упакованы в один MediatorLiveData, который предоставляется NetworkBoundResource.

Давайте подробнее рассмотрим, как NetworkBoundResource будет работать в нашем приложении. Представьте, что пользователь запустит приложение и нажмет кнопку с плавающим действием в правом нижнем углу. Приложение запустит экран добавления криптовалюты. Теперь мы можем проанализировать использование NetworkBoundResource внутри него.
Если приложение установлено только что и это его первый запуск, то в локальной базе данных не будет никаких данных. Поскольку данных для отображения нет, будет отображаться пользовательский интерфейс индикатора выполнения загрузки. Между тем приложение собирается сделать запрос на сервер через веб-службу, чтобы получить весь список криптовалют.
Если ответ будет неудачным, отобразится пользовательский интерфейс сообщения об ошибке с возможностью повторить вызов, нажав кнопку. Когда вызов запроса наконец-то будет успешным, данные ответа будут сохранены в локальной базе данных SQLite.
Если в следующий раз мы вернемся к тому же экрану, приложение загрузит данные из базы данных вместо того, чтобы снова звонить в Интернет. Но пользователь может запросить новое обновление данных, реализовав функцию обновления по запросу. Во время сетевого вызова будет отображаться старая информация о данных. Все это делается с помощью NetworkBoundResource.
Другой класс, используемый в нашем репозитории и LiveDataCallAdapter, где происходит вся «магия», - это ApiResponse. Фактически ApiResponse - это просто обычная оболочка для класса Retrofit2.Response, которая преобразует каждый ответ в экземпляр LiveData.
Внутри этого класса-оболочки, если в нашем ответе есть ошибка, мы используем библиотеку Gson для преобразования ошибки в объект JSON. Однако, если ответ был успешным, используется конвертер Gson для сопоставления объектов JSON в POJO. Мы уже добавляли его при создании экземпляра конструктора модернизации с GsonConverterFactory внутри функции Dagger AppModule provideApiService.
Скольжение для загрузки изображений
Что такое Glide? Из документов:
Glide - это быстрая и эффективная платформа для управления мультимедиа и загрузки изображений с открытым исходным кодом для Android, которая объединяет декодирование мультимедиа, кэширование памяти и дисков, а также объединение ресурсов в простой и удобный интерфейс.
Основная задача Glide - сделать прокрутку любого списка изображений максимально плавной и быстрой, но она также эффективна практически для любого случая, когда вам нужно получить, изменить размер и отобразить удаленное изображение.
Похоже на сложную библиотеку, которая предлагает множество полезных функций, которые вы не хотели бы разрабатывать самостоятельно. В приложении My Crypto Coins у нас есть несколько экранов со списками, где нам нужно отображать несколько логотипов криптовалюты - изображения, взятые из Интернета одновременно, - и при этом обеспечивать плавную прокрутку для пользователя. Так что эта библиотека идеально подходит для наших нужд. Также эта библиотека очень популярна среди разработчиков Android.
Шаги по настройке Glide в проекте приложения My Crypto Coins:
Объявить зависимости
Получите последнюю версию Glide. Опять же версии - это отдельный файл versions.gradle для проекта.
Поскольку мы хотим использовать сетевую библиотеку OkHttp в нашем проекте для всех сетевых операций, нам необходимо включить для нее конкретную интеграцию Glide вместо стандартной. Кроме того, поскольку Glide будет выполнять сетевой запрос для загрузки изображений через Интернет, нам необходимо включить разрешение INTERNET в наш AndroidManifest.xml файл, но мы уже сделали это с помощью настройки Retrofit.
Создать AppGlideModule
Glide v4, который мы будем использовать, предлагает сгенерированный API для приложений. Он будет использовать процессор аннотаций для создания API, который позволяет приложениям расширять API Glide и включать компоненты, предоставляемые библиотеками интеграции. Чтобы любое приложение могло получить доступ к сгенерированному API Glide, нам необходимо включить AppGlideModule реализацию с соответствующими аннотациями. Может быть только одна реализация сгенерированного API и только один AppGlideModule на приложение.
Давайте создадим класс, расширяющий AppGlideModule где-нибудь в вашем проекте приложения:
Даже если наше приложение не меняет никаких дополнительных настроек или не реализует какие-либо методы в AppGlideModule, нам все равно нужна его реализация для использования Glide. От вас не требуется реализовывать какие-либо методы в AppGlideModule для создания API. Вы можете оставить класс пустым, если он расширяет AppGlideModule и снабжен аннотацией @GlideModule.
Используйте API, созданный с помощью Glide
При использовании AppGlideModule приложения могут использовать API, начиная все загрузки с GlideApp.with(). Это код, который показывает, как я использовал Glide для загрузки и отображения логотипов криптовалюты на экране добавления криптовалют для всех списков криптовалют.
Как видите, вы можете начать использовать Glide с помощью всего нескольких строк кода и позволить ему делать всю тяжелую работу за вас. Это довольно просто.
Котлинские сопрограммы
При создании этого приложения мы столкнемся с ситуациями, когда мы будем выполнять трудоемкие задачи, такие как запись данных в базу данных или чтение из нее, получение данных из сети и другие. Все эти общие задачи занимают больше времени, чем позволяет основной поток платформы Android.
Основной поток - это единственный поток, который обрабатывает все обновления пользовательского интерфейса. Разработчики должны не блокировать его, чтобы избежать зависания приложения или даже сбоя из-за диалогового окна «Приложение не отвечает». Сопрограммы Kotlin решат эту проблему, введя безопасность основных потоков. Это последний недостающий элемент, который мы хотим добавить в приложение My Crypto Coins.
Сопрограммы - это функция Kotlin, которая преобразует асинхронные обратные вызовы для длительных задач, таких как доступ к базе данных или сети, в последовательный код. С помощью сопрограмм вы можете писать асинхронный код, который традиционно писался с использованием шаблона обратного вызова, с использованием синхронного стиля. Возвращаемое значение функции предоставит результат асинхронного вызова. Код, написанный последовательно, обычно легче читать и даже может использовать языковые функции, такие как исключения.
Поэтому мы собираемся использовать сопрограммы повсюду в этом приложении, где нам нужно дождаться, пока не станет доступен результат из долго выполняющейся задачи, а затем продолжить выполнение. Давайте посмотрим на одну точную реализацию нашей ViewModel, в которой мы снова попытаемся получить последние данные с сервера для наших криптовалют, представленных на главном экране.
Сначала добавьте в проект сопрограммы:
Затем мы создадим абстрактный класс, который станет базовым классом, который будет использоваться для любой модели ViewModel, которая должна иметь общие функции, такие как сопрограммы в нашем случае:
Здесь мы создаем конкретную область видимости сопрограмм, которая будет контролировать время жизни сопрограмм посредством своей работы. Как видите, область видимости позволяет вам указать диспетчера по умолчанию, который контролирует, какой поток запускает сопрограмму. Когда ViewModel больше не используется, мы отменяем viewModelJob, и при этом каждая сопрограмма, запущенная uiScope, также будет отменена.
Наконец, реализуйте функцию повтора:
Здесь мы вызываем функцию, отмеченную специальным ключевым словом Kotlin suspend для сопрограмм. Это означает, что функция приостанавливает выполнение до тех пор, пока результат не будет готов, а затем возобновляет выполнение с того места, где было остановлено с результатом. Пока он приостановлен в ожидании результата, он разблокирует поток, в котором он работает.
Кроме того, в одной функции приостановки мы можем вызвать другую функцию приостановки. Как видите, мы делаем это, вызывая новую функцию приостановки с пометкой withContext, которая выполняется в другом потоке.
Идея всего этого кода состоит в том, что мы можем комбинировать несколько вызовов, чтобы сформировать красивый последовательный код. Сначала мы запрашиваем из локальной базы данных идентификаторы принадлежащих нам криптовалют и ждем ответа. Только после того, как мы его получим, мы используем идентификаторы ответа, чтобы сделать новый вызов с помощью Retrofit, чтобы получить эти обновленные значения криптовалюты. Это наша функция повтора.
Мы сделали это! Заключительные мысли, репозиторий, приложение и презентация
Поздравляю, я рада, что вам удалось дойти до конца. Мы рассмотрели все наиболее важные моменты для создания этого приложения. В этой части было сделано много нового, и многое из этого не рассматривается в этой статье, но я везде очень хорошо прокомментировал свой код, так что вы не должны в нем заблудиться. Ознакомьтесь с окончательным кодом этой части 5 здесь, на GitHub:
Посмотреть исходный код на GitHub.
Самой большой проблемой для меня лично было не изучение новых технологий, не разработка приложения, а написание всех этих статей. На самом деле я очень доволен собой, что выполнил эту задачу. Обучение и развитие легче по сравнению с обучением других, но именно здесь вы можете понять тему еще лучше. Мой совет, если вы ищете лучший способ узнать что-то новое, - немедленно начните создавать что-то самостоятельно. Обещаю, вы быстро и многому научитесь.
Все эти статьи основаны на версии 1.0.0 приложения Kriptofolio (ранее My Crypto Coins), которое вы можете скачать в виде отдельного APK-файла здесь. Но я буду очень рад, если вы установите и оцените последнюю версию приложения напрямую из магазина:
Скачать в Google Play
Также посетите этот простой веб-сайт с презентацией, который я сделал для этого проекта:
Kriptofolio.app

Ачу! Спасибо за прочтение! Изначально я опубликовал этот пост в своем личном блоге www.baruckis.com 11 мая 2019 г.