conn.outputStream.use {
it.write(body.toByteArray())
}
这三行代码的作用是将请求体数据写入HTTP连接的输出流中:
-
conn.outputStream.use { ... }: 获取conn的输出流并使用use函数进行资源管理,确保流在使用后能被正确关闭 -
it.write(body.toByteArray()): 将传入的 [body] 字符串转换为字节数组,并通过输出流写入到HTTP请求中
val responseBody = if (conn.responseCode in 100..399) {
conn.inputStream.use { it.readBytes() }.toString(Charsets.UTF_8)
} else {
conn.errorStream.use { it.readBytes() }.toString(Charsets.UTF_8)
}
return Response(conn.responseCode, conn.headerFields, responseBody)
这段代码用于读取HTTP响应的内容并返回结果:
-
val responseBody = if (conn.responseCode in 100..399) { ... } else { ... }: 根据HTTP状态码判断使用哪个输入流来读取响应数据- 状态码在100-399之间表示成功响应,使用
conn.inputStream - 状态码在400-599之间表示错误响应,使用
conn.errorStream
- 状态码在100-399之间表示成功响应,使用
-
conn.inputStream.use { it.readBytes() }.toString(Charsets.UTF_8):- 使用
use函数安全地打开输入流并读取所有字节数据 - 将字节数组转换为UTF-8编码的字符串
- 使用
-
return Response(conn.responseCode, conn.headerFields, responseBody): 构造并返回包含状态码、响应头和响应体的 [Response] 对象
这种方式能够正确处理HTTP的成功和错误响应情况。
private val client: CloseableHttpAsyncClient = HttpAsyncClients.customHttp2()
.setTlsStrategy(
ClientTlsStrategyBuilder.create()
.setSslContext(SSLContexts.custom().loadTrustMaterial(TrustSelfSignedStrategy()).build())
.setTlsVersions(TLS.V_1_3, TLS.V_1_2)
.build()
)
.setRetryStrategy(
DefaultHttpRequestRetryStrategy(5, TimeValue.ofMilliseconds(50))
)
.evictIdleConnections(TimeValue.ofSeconds(10))
.setIOReactorConfig(
IOReactorConfig.custom()
.setSoTimeout(30, TimeUnit.SECONDS)
.setSelectInterval(TimeValue.ofMilliseconds(50))
.setTcpNoDelay(true)
.build()
)
.setDefaultRequestConfig(
RequestConfig.custom()
.setConnectionKeepAlive(TimeValue.ofMilliseconds(50))
.setHardCancellationEnabled(true)
.setConnectionRequestTimeout(50, TimeUnit.MILLISECONDS)
.setResponseTimeout(30, TimeUnit.SECONDS)
.build()
)
.setDefaultHeaders(
listOf<Header>(
BasicHeader(HttpHeaders.CONTENT_TYPE, "application/json"),
BasicHeader(HttpHeaders.CACHE_CONTROL, "no-cache"),
BasicHeader(HttpHeaders.REFERER, "no-referrer"),
)
)
.build()
这段代码是在初始化和配置一个异步HTTP客户端,具体配置如下:
-
HttpAsyncClients.customHttp2(): 创建支持HTTP/2协议的异步HTTP客户端 -
TLS安全配置:
- 使用
ClientTlsStrategyBuilder配置TLS策略 - 设置SSL上下文,通过
TrustSelfSignedStrategy信任自签名证书 - 限制TLS版本为
TLS.V_1_3和TLS.V_1_2
- 使用
-
重试策略:
setRetryStrategy设置默认重试策略,最多重试5次,间隔50毫秒
-
连接管理:
evictIdleConnections设置空闲连接清理,10秒后清理
-
IO反应器配置:
setSoTimeout设置Socket超时为30秒setSelectInterval设置选择间隔为50毫秒setTcpNoDelay启用TCP无延迟
-
请求配置:
- 连接保持活跃时间50毫秒
- 启用强制取消功能
- 连接请求超时50毫秒
- 响应超时30秒
-
默认请求头:
- 设置
Content-Type为application/json - 设置缓存控制为
no-cache - 设置
Referer为no-referrer
- 设置
这个配置创建了一个高性能、安全且具备重试机制的异步HTTP客户端。
init {
client.start()
}
fun ping(uri: URI) {
client.execute(SimpleHttpRequest.create(Method.GET, uri), null).get(5, TimeUnit.SECONDS)
}
[ping] 函数是 [AsyncConnection] 类中的一个方法,用于测试与指定 URI 的连接是否正常。
具体解释如下:
-
方法签名:
fun ping(uri: URI)- 接收一个
URI类型的参数,表示要测试连接的目标地址
- 接收一个
-
核心实现:
client.execute(SimpleHttpRequest.create(Method.GET, uri), null).get(5, TimeUnit.SECONDS)- 使用已初始化的 HTTP 客户端 [client] 发送 GET 请求
- 通过
SimpleHttpRequest.create(Method.GET, uri)创建一个简单的 GET 请求 - 调用
client.execute()执行该请求 - 使用
.get(5, TimeUnit.SECONDS)同步等待响应,超时时间为 5 秒
-
作用:
- 验证与目标服务器的连接状态
- 如果在 5 秒内收到响应,则表示连接正常
- 如果超时或出现异常,则说明连接存在问题
这个方法主要用于健康检查或连接状态验证,确保服务可用性。
private fun send(
requestProducer: AsyncRequestProducer,
uri: URI,
stat: UsageStatistic,
dataReceiveEnded: (String) -> Unit,
dataReceived: (String, String) -> Unit,
errorDataReceived: (JsonObject) -> Unit,
failedDataReceiveEnded: (Throwable?) -> Unit = {},
requestId: String = ""
): CompletableFuture<Future<*>> {
var canceled = false;
return CompletableFuture.supplyAsync {
return@supplyAsync client.execute(
requestProducer,
object : AbstractBinResponseConsumer<String>() {
private var bufferStr = ""
private var isStreaming = false
override fun releaseResources() {
}
override fun capacityIncrement(): Int {
return 128
}
override fun informationResponse(
response: HttpResponse?,
context: org.apache.hc.core5.http.protocol.HttpContext?
) {
}
override fun data(src: ByteBuffer?, endOfStream: Boolean) {
src ?: return
if(canceled) return;
val part = Charset.forName("UTF-8").decode(src)
bufferStr += part
if (part.startsWith(STREAMING_PREFIX)) {
isStreaming = true
try {
val data = Gson().fromJson(bufferStr, JsonObject::class.java)
errorDataReceived(data)
return
} catch (_: JsonSyntaxException) {
}
val (dataPieces, maybeLeftOverBuffer) = lookForCompletedDataInStreamingBuf(bufferStr)
dataPieces.forEach { dataReceived(it, requestId) }
if (maybeLeftOverBuffer == null) {
return
} else {
bufferStr = maybeLeftOverBuffer
}
}
if (!isStreaming && endOfStream) {
if (bufferStr.isNotEmpty()) {
dataReceived(bufferStr, requestId)
}
}
}
override fun start(response: HttpResponse?, contentType: ContentType?) {
}
override fun buildResult(): String {
return bufferStr
}
override fun failed(cause: Exception?) {}
},
object : FutureCallback<String> {
override fun completed(result: String) {
dataReceiveEnded(result)
}
override fun failed(ex: java.lang.Exception?) {
if (ex !is SMCExceptions)
UsageStats?.addStatistic(false, stat, uri.toString(), ex.toString())
if (ex is java.net.SocketException ||
ex is java.net.UnknownHostException) {
InferenceGlobalContext.status = ConnectionStatus.DISCONNECTED
}
failedDataReceiveEnded(ex)
}
override fun cancelled() {
// TODO: figure out how to stop the stream fro the lsp
canceled = true
dataReceiveEnded("Canceled")
}
}
)
}
}
ApplicationManager.getApplication().invokeLater {} 是 IntelliJ Platform 提供的一个重要机制,用于在事件调度线程(EDT)上执行代码。
让我详细解释一下它的作用:
-
线程安全: IntelliJ 平台的 UI 操作必须在特定的事件调度线程上执行。
invokeLater确保代码块在正确的线程上运行,避免线程安全问题。 -
异步执行: 当前代码可能在后台线程运行(如网络请求、文件操作等),但需要更新 UI 时,就需要使用
invokeLater将操作推迟到 UI 线程执行。 -
非阻塞: 与
invokeAndWait不同,invokeLater是非阻塞的,不会等待 UI 操作完成就继续执行后续代码。
例如:
// 在后台线程处理一些逻辑
someBackgroundProcessing()
// 当需要更新 UI 时,使用 invokeLater
ApplicationManager.getApplication().invokeLater {
// 更新 UI 组件
myTextField.text = "更新的内容"
myPanel.repaint()
}
// 这行代码会立即执行,不需要等待上面的 UI 更新完成
continueWithOtherWork()
ApplicationManager.getApplication().invokeLater {} 是专属于 IntelliJ Platform(JetBrains 系列 IDE 的平台)的 API,不能在 Android 应用开发中使用。
在 Android 开发中,如果你需要在 UI 线程上执行代码,应该使用以下方法之一:
- Activity.runOnUiThread():
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
// 更新 UI 的代码
textView.setText("新文本");
}
});
- View.post():
view.post(new Runnable() {
@Override
public void run() {
// 更新 UI 的代码
view.setVisibility(View.VISIBLE);
}
});
- Handler:
Handler mainHandler = new Handler(Looper.getMainLooper());
mainHandler.post(new Runnable() {
@Override
public void run() {
// 更新 UI 的代码
}
});
- 在 Kotlin 中使用协程:
lifecycleScope.launch(Dispatchers.Main) {
// 更新 UI 的代码
textView.text = "新文本"
}
这些是 Android 中标准的 UI 线程操作方式,与 IntelliJ Platform 的 API 不同。每种方法都有其适用场景:
- Activity.runOnUiThread(): 当你持有 Activity 实例时使用
- View.post(): 当你需要在特定 View 上执行操作时使用
- Handler: 更底层的线程间通信方式
- 协程: 现代 Android 开发推荐的方式,更简洁易读