Котлин корутины. Часть 4. Переход callback API на корутины

В третьей части этой серии вы узнали, как тестировать корутины через поведение. В этой части мы конвертируем существующий API, который работает на основе методов обратного вызова (колбеков).  Заменим колбеки на корутины .

Чтобы на практике увидеть работу с Kotlin Coroutines, Room и архитектурными компонентами, записывайтесь на продвинутый курс по разработке приложения «Чат-мессенжер»

Откройте проект в Android Studio

Для начала откройте проект kotlin-coroutines-repository в Android Studio. Исходный код можно найти в первом уроке.

Это приложение использует компоненты архитектуры и расширяет предыдущий проект для реализации уровня данных, который использует как сетевую, так и локальную базу данных. При щелчке по основному виду он выбирает новый заголовок из сети, сохраняет его в базе данных и отображает его на экране. Найдите минутку, чтобы ознакомиться с новыми классами.

  1. MainDatabase реализует базу данных с использованием Room, которая сохраняет и загружает Title.
  2. MainNetwork реализует сетевой API, который выбирает новый заголовок. Он использует поддельную сетевую библиотеку, определенную в FakeNetworkLibrary.kt для того, чтобы получать названия. Сетевая библиотека будет случайным образом возвращать ошибки.
  3. TitleRepository реализует единый API для извлечения или обновления заголовка путем объединения данных из сети и базы данных.
  4. MainViewModelTest определяет тест для MainViewModel.
  5. FakeNetworkCallAwaitTest это тест, который мы закончим позже в этом уроке.

Исследуйте существующий callback API

Откройте MainNetwork.kt и посмотрите на декларацию fetchNewWelcome()

Откройте TitleRepository.kt чтобы увидеть, как fetchNewWelcome используется для выполнения сетевого вызова с использованием колбеков.

Эта функция возвращает FakeNetworkCall, что позволяет регистрировать слушателя для этого сетевого запроса. Вызов fetchNewWelcome запустит длительный сетевой запрос в другом потоке и вернет объект, который устанавливает addOnResultListener. Ваш код передает обратный вызов addOnResultListener который будет вызван, когда запрос завершится успехом или ошибкой.

Преобразование callback API в suspend функцию

Функция refreshTitle в настоящее время реализована с использованием колбеков на FakeNetworkCall. Цель этого упражнения — представить наш сетевой API-интерфейс как suspend функцию, чтобы refreshTitle можно было переписать с использованием корутин.

Для этого Kotlin предоставляет функцию suspendCoroutine, которая используется для преобразования API на основе колбеков в suspend функции.

Вызов suspendCoroutine немедленно приостановит текущую корутину suspendCoroutine и предоставит вам объект continuation, который вы можете использовать для возобновления корутины. Continuation содержит весь контекст, необходимый для продолжения или возобновления приостановленной корутины.

Continuation , которое предоставляет suspendCoroutine, имеет две функции: resume и resumeWithException. Вызов любой функции приведет к немедленному возобновлению suspendCoroutine.

Вы можете использовать suspendCoroutine для приостановки перед ожиданием обратного вызова. Затем после колбека вызывается resume вызов или resumeWithException для возобновления с результатом колбека.

Пример suspendCoroutine выглядит следующим образом:

В этом примере показано, как использовать suspendCoroutine для преобразования API на основе обратного вызова при вызове в функцию приостановки. Теперь вы можете использовать Call непосредственно в коде на основе сопрограмм, например:

Вы можете использовать этот шаблон для предоставления функции приостановки на FakeNetworkCall, которая позволяет вам использовать сетевой API на основе колбеков в корутинах.

Как насчет отмены?

suspendCoroutine — хороший выбор, когда вам не нужно поддерживать cancellation. Как правило, однако, cancellation является проблемой, и вы можете использовать suspendCancellableCoroutine для распространения cancellation в библиотеки, которые поддерживают cancellation в API на основе колбеков.

suspendCoroutine для преобразования callback API в корутины

Прокрутите до конца TitleRepository.kt и найдите TODO для реализации функции расширения.

Замените этот TODO этой функцией расширения на FakeNetworkCall <T>:

Эта suspend функция использует suspendCoroutine для преобразования API на основе колбеков в suspend функцию. Корутины могут вызвать await и будут немедленно приостановлены, пока результат сети не будет готов. Результатом сети является возвращаемое значение await, и ошибки будут вызывать исключение.

Вы можете использовать это так:

Стоит потратить секунду, чтобы прочитать сигнатуру suspend функции. Ключевое слово suspend сообщает Kotlin, что это доступно корутинам. В результате он может вызывать другие suspend функции, такие как suspendCoroutine. Остальная часть объявления, fun <T> FakeNetworkCall <T> .await (), определяет функцию расширения, которая вызывает await для любого FakeNetworkCall. На самом деле он не изменяет класс, но при вызове из Kotlin он отображается как public метод. Тип возвращаемого значения await — T, который указывается после имени функции.

Что такое функция расширения?

Если вы новичок в Kotlin, функции расширения могут быть новой концепцией. Функции расширения не модифицируют класс, вместо этого они вводят новую функцию, которая принимает this в качестве первого аргумента.

fun <T> await(this: FakeNetworkCall<T>): T

Внутри тела await функции, this связан с переданным FakeNetworkCall<T> . Вот как await  вызывает addOnResultListener. Он использует неявный this так же, как метод члена.

Все вместе эта подпись означает, что мы добавляем функцию приостановки, называемую await (), в класс, который изначально не был создан для корутин. Этот подход можно использовать для обновления API на основе колбеков для поддержки корутин без изменения реализации.

В следующем упражнении мы напишем тесты для await () и узнаем, как вызывать корутины непосредственно из тестов.

Добавить комментарий