Retrofit+OkHttp(Kotlin) Post请求后Socket超时崩溃问题求助
问题分析与解决方案
核心问题
你遇到的SocketTimeoutException并非单纯超时问题——服务器已收到请求,但Android端因响应体处理不当、协程生命周期未绑定,导致Socket被提前关闭,最终引发崩溃。延长超时只是延迟了错误触发,无法解决根本问题。
错误根源拆解
从日志里的Caused by: java.net.SocketException: Socket closed可以明确:
- 协程作用域不安全:使用
CoroutineScope(Dispatchers.IO).launch创建的全局协程未绑定组件生命周期,若组件(如Service/Activity)销毁,协程会被强制取消,直接关闭Socket。 - 响应体重复关闭:代码中先后在
finally块和协程末尾两次调用response.body()?.close(),重复关闭会触发Socket异常。 - 主线程阻塞读取响应:在
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
相关产品推荐
相关产品推荐

