如何从 Java 调用协程(挂起函数和流程)?

Chr*_*anB 7 java kotlin kotlin-coroutines

我决定写这个问题,因为目前还没有关于这个主题的最佳实践。

我们提供了一个 Android SDK,它通过协程实现异步调用。我希望我们的客户使用 Java 或标准 Kotlin 的挂起函数Flow

我知道有kotlinx-coroutines-jdk8,但这只能在 Android Api 级别 24 上使用,而我们的 SDK 支持 Android 低至 Api 级别 21。所以目前这不是一个选项。

我的想法是通过提供简单的回调 API 来连接 Java(或标准 Kotlin)和协程的世界。

我想知道我的以下方法是否是一个好的解决方案。有什么缺点或危险吗?我有什么选择可以让客户端调用协程函数而不强迫他们自己使用协程?

现在让我们开始吧。首先,我向您展示一些接口和辅助函数,稍后我可以使用它们来映射挂起函数和流程。

消除

我需要确保 Coroutine 可以从 Java 中取消。所以我创建了一个Cancelable界面。

interface Cancelable {
  fun cancel()
}
Run Code Online (Sandbox Code Playgroud)

CancelableJob该接口通过包含并隐藏Job要取消的内容来实现。

class CancelableJob(private val job: Job) : Cancelable {
  override fun cancel() {
    job.cancel()
  }
}
Run Code Online (Sandbox Code Playgroud)

启动一个新的协程

每次客户端调用一个函数时,我都会启动一个新的协程。为此,我创建了一个顶级函数launchCancelableJob。该函数获取一个挂起块并返回一个Cancelable. 协程将在 上启动Dispatchers.Main,因此可以在 UI 线程上观察所有结果以及SupervisedJob.

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

  return CancelableJob(job)
}
Run Code Online (Sandbox Code Playgroud)

将挂起功能和流程桥接至 Java 世界

现在是时候提供一个桥接函数,该函数本身不是挂起函数,而是启动一个协程并返回一个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)
  }
}
Run Code Online (Sandbox Code Playgroud)

我也可以用 Flow 做同样的事情。

// 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)
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

用法

上面的函数可以从 Java 中调用,如下所示:

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

如果不再需要它可以取消,例如:

cancelable.cancel();
Run Code Online (Sandbox Code Playgroud)

这是我的方法。我真的很期待您的意见或者更好的解决方案。感谢您的阅读,我知道这是一个很长的问题。