android - 如何在 Java 代码中使用 Kotlin 协程实现 NIO Socket(客户端)?
问题描述
我想使用 Kotlin(v1.3.0)协程和 java.nio.channels。SocketChannel (NIO) 替换connect
Android 中的 Socket(阻塞 IO)。因为这样可以节省很多线程。
下面的代码无法运行,因为job.await()
它是 Kotlin 中的挂起函数,它只能在 Ktolin 协程块中调用。像launch{..}
,async{..}
。
// this function will be called by Java Code
fun connect(address: InetSocketAddress, connectTimeout: Int): SocketChannel {
// Start a new connection
// Create a non-blocking socket channel
val socketChannel = SocketChannel.open()
socketChannel.configureBlocking(false)
// async calls NIO connect function
val job = GlobalScope.async(oneThreadCtx) {
aConnect(socketChannel, address)
}
// I what to suspend(NOT block) current Java Thread, until connect is success
job.await()
return socketChannel
}
但是,我尝试将runBlocking{..}
这个函数用作 Java 中的普通函数。但job.await
阻止了当前的 Java 线程,而不是暂停。
那么,我应该如何使用 Kotlin(v1.3.0) 协程实现这个功能?
解决方案
正如 Marko 指出的那样,即使该阻塞操作在异步协程中,您的代码仍将最终阻塞线程。要使用 Java 和 Kotlin 真正获得所需的异步行为,您需要使用Socket Channel的异步版本
有了这个,您可以获得真正的异步套接字处理。使用该类和 Kotlin 的suspendCoroutine构建器方法,您可以将异步处理程序转换为可挂起的调用。
这是一个实现读取的示例:
class TcpSocket(private val socket: AsynchronousSocketChannel) {
suspend fun read(buffer: ByteBuffer): Int {
return socket.asyncRead(buffer)
}
fun close() {
socket.close()
}
private suspend fun AsynchronousSocketChannel.asyncRead(buffer: ByteBuffer): Int {
return suspendCoroutine { continuation ->
this.read(buffer, continuation, ReadCompletionHandler)
}
}
object ReadCompletionHandler : CompletionHandler<Int, Continuation<Int>> {
override fun completed(result: Int, attachment: Continuation<Int>) {
attachment.resume(result)
}
override fun failed(exc: Throwable, attachment: Continuation<Int>) {
attachment.resumeWithException(exc)
}
}
}
您可以选择删除我在这里所做的包装,然后asyncRead
在 AsynchronousSocketChannel 上公开一个方法,如下所示:
suspend fun AsynchronousSocketChannel.asyncRead(buffer: ByteBuffer): Int {
return suspendCoroutine { continuation ->
this.read(buffer, continuation, ReadCompletionHandler)
}
}
object ReadCompletionHandler : CompletionHandler<Int, Continuation<Int>> {
override fun completed(result: Int, attachment: Continuation<Int>) {
attachment.resume(result)
}
override fun failed(exc: Throwable, attachment: Continuation<Int>) {
attachment.resumeWithException(exc)
}
}
这完全取决于品味以及您的设计目标到底是什么。您应该能够为初始连接实现类似的方法,就像我在此处阅读时所做的那样。
推荐阅读
- python - 为什么我最初为 BST 将 root 设置为 None 时会出错
- r - 如何定位列并将其转换为百分比
- vue.js - 使用
在Vue中标记为第一个孩子 tag in Vue 2 In Vue,js 2, I would like to use this pattern:
<template> <component :is="tagType"> ... </component> </template>
Where tagT
- javascript - 在 Web 应用程序中实现端到端 PKI
- python - 有没有更简单的方法可以从三组数字中找到等差数列?
- c++ - 为什么在函数参数中分配新指针不好?
- python - ByBit API:有没有办法在开仓后下单止盈止损单?
- java - Java - 在方法中允许一个线程而无需等待
- sql-server - 如何选择所有参数来运行报告,但让它们独立运行
- javascript - 更新状态时如何修复 React.useEffect 和 useCallback 循环循环?