Как вызвать сопрограммы (приостановить функцию и поток) из Java?

#java #kotlin #kotlin-coroutines

#java #kotlin #kotlin-сопрограммы

Вопрос:

Я решил написать этот вопрос, поскольку пока нет лучших практик по этой теме.

Мы предоставляем Android SDK, который реализует асинхронные вызовы с сопрограммами. Я хочу, чтобы наши клиенты использовали приостанавливающие функции и Flow из Java или стандартного Kotlin.

Я знаю, что есть kotlinx-coroutines-jdk8, но это можно использовать только с 24-го уровня Api Android, а наш SDK поддерживает Android вплоть до 21-го уровня Api. Так что на данный момент это не вариант.

Моя идея состояла в том, чтобы соединить миры Java (или стандартного Kotlin) и сопрограмм, предоставив простой API обратного вызова.

Я хотел бы знать, является ли мой следующий подход хорошим решением. Есть ли какие-то недостатки или опасности? Какая альтернатива у меня была бы, чтобы заставить клиентов вызывать функции сопрограмм, не заставляя их самостоятельно использовать сопрограммы?

А теперь давайте начнем. Сначала я покажу вам некоторые интерфейсы и вспомогательные функции, которые я могу использовать позже для сопоставления функций приостановки и потока.

Отмена бронирования

Мне нужно убедиться, что сопрограмма может быть отменена из Java. Итак, я создал Cancelable интерфейс.

 interface Cancelable {
  fun cancel()
}
 

Этот интерфейс реализуется CancelableJob тем, что содержит и скрывает Job подлежащее отмене.

 class CancelableJob(private val job: Job) : Cancelable {
  override fun cancel() {
    job.cancel()
  }
}
 

Запустите новую сопрограмму

Каждый раз, когда клиент вызывает функцию, я запускаю новую сопрограмму. Для этого я создаю функцию верхнего уровня launchCancelableJob . Эта функция получает приостанавливающий блок и возвращает a Cancelable . Сопрограмма будет запущена Dispatchers.Main , поэтому все результаты можно будет наблюдать в потоке пользовательского интерфейса вместе с SupervisedJob .

 fun launchCancelableJob(block: suspend () -> Unit): Cancelable {
  val job: Job = CoroutineScope(Dispatchers.Main   SupervisorJob()).launch {
    block.invoke()
  }

  return CancelableJob(job)
}
 

Перенесите функцию приостановки и поток в мир Java

Теперь пришло время предоставить функцию моста, которая сама по себе не является функцией приостановки, но запускает сопрограмму и возвращает a Cancelable . После заданного обратного вызова результат будет доставлен вызывающему абоненту.

 // normal coroutine api
suspend fun generateQrCode(): QrCode

// bridge function - to be called from Java
fun generateQrCode(callback: (QrCode) -> Unit): Cancelable {
  return launchCancelableJob {
    val qrCode: QrCode = generateQrCode()
    callback(qrcode)
  }
}
 

То же самое я мог бы сделать с потоком.

 // normal coroutine api
fun generateQrCodes(): Flow<QrCode>

// bridge function - to be called from Java
fun generateQrCodes(callback: (QrCode) -> Unit): Cancelable {
  return launchCancelableJob {
    generateQrCodes().collect { qrCode: QrCode ->
      callback(qrcode)
    }
  }
}
 

Использование

Вышеупомянутая функция может быть вызвана из Java следующим образом:

 Cancelable cancelable = generateQrCode(new Function1<QrCode, Unit>() {
  @Override
  public Unit invoke(QrCode qrCode) {
    // show the qrCode
    return Unit.INSTANCE;
  }
});
 

И если он больше не нужен, его можно отменить, например:

 cancelable.cancel();
 

Это мой подход. Я действительно с нетерпением жду ваших мнений или, возможно, лучших решений. Спасибо за чтение, я знаю, что это был очень длинный вопрос.

Комментарии:

1. Возможно, стоит задать этот вопрос на CodeReview , если вы ищете отзывы о качестве вашего кода.

2. @AlexRudenko Я согласен, что этот пост слишком широк / субъективен для SO и может подойти для CR. Было бы здорово, если бы было также предложение типа » Пожалуйста, прочитайте соответствующие страницы справочного центра, например: » О каких темах я могу спросить здесь? ‘ и ‘ Как мне задать хороший вопрос? «. Стандарт сайта на CR заключается в том, что заголовок описывает, что делает код, а не цели проверки, поэтому их следует изменить, и напоминает о том, что весь код должен быть размещен для полной перспективы проверки.