You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Retrofit+OkHttp(Kotlin) Post请求后Socket超时崩溃问题求助

问题分析与解决方案

核心问题

你遇到的SocketTimeoutException并非单纯超时问题——服务器已收到请求,但Android端因响应体处理不当、协程生命周期未绑定,导致Socket被提前关闭,最终引发崩溃。延长超时只是延迟了错误触发,无法解决根本问题。

错误根源拆解

从日志里的Caused by: java.net.SocketException: Socket closed可以明确:

  1. 协程作用域不安全:使用CoroutineScope(Dispatchers.IO).launch创建的全局协程未绑定组件生命周期,若组件(如Service/Activity)销毁,协程会被强制取消,直接关闭Socket。
  2. 响应体重复关闭:代码中先后在finally块和协程末尾两次调用response.body()?.close(),重复关闭会触发Socket异常。
  3. 主线程阻塞读取响应:在withContext(Dispatchers.Main)中读取response.body()?.string()属于IO阻塞操作,不仅会卡主线程,还可能在协程取消时抛出异常。

修复步骤

1. 替换协程作用域

使用组件绑定的协程作用域,避免全局协程的生命周期问题:

  • Activity/Fragment用lifecycleScope
  • ViewModel用viewModelScope

2. 规范响应体处理

用Kotlin的use函数自动管理响应体关闭,避免手动重复操作;同时把响应体读取放在IO线程,不阻塞主线程。

3. 优化服务器检查

确认Node.js服务器接收请求后及时返回响应并关闭连接——如果服务器未正确结束请求,Android端会一直等待直到超时。

修复后代码示例

fun sendTheTextMessage(sender: String, message: String, date: String, receiver: String) {
    try {
        // 配置OkHttpClient
        val httpClient = OkHttpClient.Builder()
            .callTimeout(2, TimeUnit.MINUTES)
            .connectTimeout(20, TimeUnit.SECONDS)
            .readTimeout(30, TimeUnit.SECONDS)
            .writeTimeout(30, TimeUnit.SECONDS)
            .build()

        // 配置Retrofit
        val retrofit = Retrofit.Builder()
            .baseUrl("http://192.168.1.234:3001")
            .client(httpClient)
            .build()

        val service = retrofit.create(APIService::class.java)

        // 构建请求体
        val jsonObject = JSONObject().apply {
            put("sender", sender)
            put("message", message)
            put("date", date)
            put("receiver", receiver)
        }
        val requestBody = jsonObject.toString()
            .toRequestBody("application/json".toMediaTypeOrNull())

        // 使用组件绑定的协程作用域(以lifecycleScope为例)
        lifecycleScope.launch(Dispatchers.IO) {
            val responseResult = runCatching { service.createEmployee(requestBody) }
            
            responseResult.fold(
                onSuccess = { response ->
                    if (response.isSuccessful) {
                        // use函数自动关闭响应体,无需手动调用close
                        response.body()?.use { body ->
                            val rawJson = body.string()
                            val prettyJson = GsonBuilder().setPrettyPrinting().create()
                                .toJson(JsonParser.parseString(rawJson))
                            
                            // 切回主线程打印日志
                            withContext(Dispatchers.Main) {
                                Log.d("Pretty Printed JSON :", prettyJson)
                            }
                        }
                    } else {
                        withContext(Dispatchers.Main) {
                            Log.e("RETROFIT_ERROR", response.code().toString())
                        }
                    }
                },
                onFailure = { error ->
                    withContext(Dispatchers.Main) {
                        Log.e("REQUEST_ERROR", error.localizedMessage ?: "未知请求错误")
                    }
                }
            )
        }
    } catch (e: Exception) {
        Log.e("INIT_ERROR", e.localizedMessage ?: "初始化失败")
    }
}

额外优化建议

给Retrofit添加Gson转换器,省去手动构建JSONObject的麻烦:

// 先添加依赖:implementation 'com.squareup.retrofit2:converter-gson:xxx'
val retrofit = Retrofit.Builder()
    .baseUrl("http://192.168.1.234:3001")
    .addConverterFactory(GsonConverterFactory.create())
    .client(httpClient)
    .build()

// 定义数据类作为请求体
data class MessageRequest(
    val sender: String,
    val message: String,
    val date: String,
    val receiver: String
)

// 直接传入数据类,无需手动处理JSON
val request = MessageRequest(sender, message, date, receiver)
val response = service.createEmployee(request)

内容的提问来源于stack exchange,提问作者Goldgamer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 06:35:32