Android中Ktor Client获HTTP 200响应仍抛出IOException问题
Android端Ktor Client调用接口抛出EOFException问题
问题描述
Android端调用接口时,网络 inspector显示接口返回HTTP 200状态码,设备也收到了预期的推送通知,但Ktor Client却抛出IOException,具体为java.io.EOFException,日志显示代码执行到POINT 1后触发异常。
相关代码
return flow { try { Timber.e("POINT, 1") val response: HttpResponse = httpClient.post(url) { setBody(enrolmentRequestData) } Timber.e("POINT, 1 2, ${response.status.value}") Timber.e("POINT, 1 2, Response body: ${response.bodyAsText()}") // 尝试打印响应体 if (response.status == HttpStatusCode.OK) { emit(Result.Success(Unit)) } else { handleErrorResponse(response.status.value) } } catch (e: ResponseException) { Timber.e("ResponseException: ${e.response.status.value}, ${e.message}") handleErrorResponse(e.response.status.value) } catch (e: JSONException) { Timber.e("JSONException: ${e.message}") emit(Result.Error(DataError.Network.JSON_PARSE_EXCEPTION)) } catch (e: IOException) { Timber.e("IOException: ${e.message}, ${e.stackTrace}}") e.printStackTrace() // 打印完整堆栈便于分析 emit(Result.Error(DataError.Network.NO_INTERNET)) } catch (e: Exception) { Timber.e("Unknown Exception: ${e.message}, ${e.stackTrace}") emit(Result.Error(DataError.Network.UNKNOWN_ERROR)) } }
日志信息
E IOException: null, [Ljava.lang.StackTraceElement;@15c8851} 2024-06-28 22:15:39.704 24066-24066 System.err eu.europa.ec.ecas W java.io.EOFException 2024-06-28 22:15:39.704 24066-24066 System.err eu.europa.ec.ecas W at java.lang.reflect.Constructor.newInstance0(Native Method) 2024-06-28 22:15:39.704 24066-24066 System.err eu.europa.ec.ecas W at java.lang.reflect.Constructor.newInstance(Constructor.java:343) 2024-06-28 22:15:39.704 24066-24066 System.err eu.europa.ec.ecas W at io.ktor.utils.io.ExceptionUtilsJvmKt$createConstructor$$inlined$safeCtor$3.invoke(SourceFile:81) 2024-06-28 22:15:39.704 24066-24066 System.err eu.europa.ec.ecas W at io.ktor.utils.io.ExceptionUtilsJvmKt$createConstructor$$inlined$safeCtor$3.invoke(SourceFile:90) 2024-06-28 22:15:39.704 24066-24066 System.err eu.europa.ec.ecas W at io.ktor.utils.io.ExceptionUtilsJvmKt.tryCopyException(SourceFile:66) 2024-06-28 22:15:39.704 24066-24066 System.err eu.europa.ec.ecas W at io.ktor.utils.io.ByteBufferChannelKt.rethrowClosed(SourceFile:2404) 2024-06-28 22:15:39.704 24066-24066 System.err eu.europa.ec.ecas W at io.ktor.utils.io.ByteBufferChannelKt.access$rethrowClosed(SourceFile:1) 2024-06-28 22:15:39.705 24066-24066 System.err eu.europa.ec.ecas W at io.ktor.utils.io.ByteBufferChannel.readRemaining$suspendImpl(SourceFile:2063) 2024-06-28 22:15:39.705 24066-24066 System.err eu.europa.ec.ecas W at io.ktor.utils.io.ByteBufferChannel.readRemaining(Unknown Source:0) 2024-06-28 22:15:39.705 24066-24066 System.err eu.europa.ec.ecas W at io.ktor.utils.io.ByteReadChannel$DefaultImpls.readRemaining$default(SourceFile:88) 2024-06-28 22:15:39.705 24066-24066 System.err eu.europa.ec.ecas W at io.ktor.client.call.SavedCallKt.save(SourceFile:73) 2024-06-28 22:15:39.705 24066-24066 System.err eu.europa.ec.ecas W at io.ktor.client.statement.HttpStatement$execute$4.invokeSuspend(SourceFile:63) 2024-06-28 22:15:39.705 24066-24066 System.err eu.europa.ec.ecas W at io.ktor.client.statement.HttpStatement$execute$4.invoke(SourceFile:0) 2024-06-28 22:15:39.705 24066-24066 System.err eu.europa.ec.ecas W at io.ktor.client.statement.HttpStatement$execute$4.invoke(SourceFile:0) 2024-06-28 22:15:39.705 24066-24066 System.err eu.europa.ec.ecas W at io.ktor.client.statement.HttpStatement.execute(SourceFile:50) 2024-06-28 22:15:39.705 24066-24066 System.err eu.europa.ec.ecas W at io.ktor.client.statement.HttpStatement$execute$1.invokeSuspend(Unknown Source:12) 2024-06-28 22:15:39.705 24066-24066 System.err eu.europa.ec.ecas W at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(SourceFile:33) 2024-06-28 22:15:39.705 24066-24066 System.err eu.europa.ec.ecas W at kotlinx.coroutines.DispatchedTask.run(SourceFile:102) 2024-06-28 22:15:39.705 24066-24066 System.err eu.europa.ec.ecas W at kotlinx.coroutines.EventLoop.processUnconfinedEvent(SourceFile:65) 2024-06-28 22:15:39.705 24066-24066 System.err eu.europa.ec.ecas W at kotlinx.coroutines.internal.DispatchedContinuation.resumeWith(SourceFile:199) 2024-06-28 22:15:39.705 24066-24066 System.err eu.europa.ec.ecas W at io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith(SourceFile:135) 2024-06-28 22:15:39.705 24066-24066 System.err eu.europa.ec.ecas W at io.ktor.util.pipeline.SuspendFunctionGun.loop(SourceFile:109) 2024-06-28 22:15:39.705 24066-24066 System.err eu.europa.ec.ecas W at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(SourceFile:11) 2024-06-28 22:15:39.705 24066-24066 System.err eu.europa.ec.ecas W at io.ktor.util.pipeline.SuspendFunctionGun$continuation$1.resumeWith(SourceFile:59) 2024-06-28 22:15:39.705 24066-24066 System.err eu.europa.ec.ecas W at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(SourceFile:46) 2024-06-28 22:15:39.705 24066-24066 System.err eu.europa.ec.ecas W at kotlinx.coroutines.DispatchedTask.run(SourceFile:102) 2024-06-28 22:15:39.705 24066-24066 System.err eu.europa.ec.ecas W at android.os.Handler.handleCallback(Handler.java:959) 2024-06-28 22:15:39.705 24066-24066 System.err eu.europa.ec.ecas W at android.os.Handler.dispatchMessage(Handler.java:100) 2024-06-28 22:15:39.705 24066-24066 System.err eu.europa.ec.ecas W at android.os.Looper.loopOnce(Looper.java:232) 2024-06-28 22:15:39.705 24066-24066 System.err eu.europa.ec.ecas W at android.os.Looper.loop(Looper.java:317) 2024-06-28 22:15:39.705 24066-24066 System.err eu.europa.ec.ecas W at android.app.ActivityThread.main(ActivityThread.java:8592) 2024-06-28 22:15:39.706 24066-24066 System.err eu.europa.ec.ecas W at java.lang.reflect.Method.invoke(Native Method) 2024-06-28 22:15:39.706 24066-24066 System.err eu.europa.ec.ecas W at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580) 2024-06-28 22:15:39.706 24066-24066 System.err eu.europa.ec.ecas W at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:878)
问题原因分析
从堆栈日志看,异常发生在Ktor的SavedCallKt.save环节,读取响应体时触发EOFException,可能的原因包括:
- 服务器返回200后提前关闭连接,导致Ktor读取响应体时没有更多数据
- 服务器返回的
Content-Length头部与实际响应体长度不匹配,Ktor预期读取更多数据但已到结尾 - Ktor客户端默认启用了响应保存功能,在保存响应过程中读取空响应体或不完整响应体时触发异常
解决方案
单独捕获EOFException并正确处理
因为实际请求已经成功(收到推送、返回200),不应将此异常归为无网络错误,而是视为请求成功:catch (e: EOFException) { Timber.e("EOFException: 响应体读取完成,请求实际已成功") emit(Result.Success(Unit)) } catch (e: IOException) { Timber.e("IOException: ${e.message}, ${e.stackTrace}}") e.printStackTrace() emit(Result.Error(DataError.Network.NO_INTERNET)) }跳过响应体读取(如果不需要响应内容)
在获取响应后直接调用discardContent(),避免读取响应体触发异常:val response: HttpResponse = httpClient.post(url) { setBody(enrolmentRequestData) } response.discardContent() // 丢弃响应体,避免读取操作禁用Ktor的SavedCall功能
如果不需要保存请求响应记录,可以在客户端配置中关闭此功能:val httpClient = HttpClient(OkHttp) { install(HttpSend) { saveCallToHistory = false } }检查服务器响应头
确认服务器返回的Content-Length是否准确,或者使用分块传输编码(Transfer-Encoding: chunked)避免长度不匹配问题。
内容的提问来源于stack exchange,提问作者Rob
相关产品推荐
相关产品推荐

