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

Kotlin协程与Retrofit2:HTTP请求完成后程序阻塞一分钟才退出的问题排查与解决咨询

Kotlin协程与Retrofit2:HTTP请求完成后程序阻塞一分钟才退出的问题排查与解决咨询

嘿,我来帮你分析这个问题并给出解决方案!

为什么程序不会立即退出?

问题出在OkHttp的线程池上——Retrofit底层依赖OkHttp处理HTTP请求,而OkHttp默认的Dispatcher(管理请求线程的核心组件)使用的是非守护线程。当你的HTTP请求完成后,这些线程不会立刻销毁,而是会保持空闲状态等待新请求,默认的空闲超时时间正好是60秒。

JVM的退出规则是:只有当所有非守护线程都终止时,JVM才会停止运行。所以哪怕你的runBlocking作用域已经执行完所有代码(打印完posts),OkHttp的空闲线程还在后台挂着,导致程序必须等满一分钟才会自动退出。

怎么让程序在请求完成后立刻退出?

这里有几个实用的解决办法,你可以根据场景选择:

方法1:自定义OkHttp客户端,缩短空闲超时时间

手动配置OkHttp的Dispatcher,把空闲线程的超时时间改短(比如1秒),这样线程会快速被回收,程序就能及时退出:

import okhttp3.OkHttpClient
import retrofit2.http.GET
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import kotlinx.coroutines.*
import java.util.concurrent.TimeUnit

data class Post(
    val userId: Int,
    val id: Int,
    val title: String
)

interface ApiService {
    @GET("posts")
    suspend fun getPosts(): List<Post>
}

object RetrofitClient {
    private const val BASE_URL = "https://jsonplaceholder.typicode.com/"
    
    // 自定义OkHttp客户端,设置Dispatcher的空闲超时
    private val okHttpClient = OkHttpClient.Builder()
        .dispatcher(
            okhttp3.Dispatcher().apply {
                // 设置空闲线程超时为1秒
                idleConnectionTimeout(1, TimeUnit.SECONDS)
            }
        )
        .build()

    val instance: ApiService by lazy {
        Retrofit.Builder()
            .baseUrl(BASE_URL)
            .client(okHttpClient) // 替换默认的OkHttp客户端
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(ApiService::class.java)
    }
}

fun main() = runBlocking {
    val posts = RetrofitClient.instance.getPosts()
    posts.forEach { println(it) }
}

方法2:手动关闭OkHttp的Dispatcher

在请求完成后,主动关闭OkHttp的Dispatcher,强制终止所有空闲线程:

fun main() = runBlocking {
    val posts = RetrofitClient.instance.getPosts()
    posts.forEach { println(it) }
    
    // 获取Retrofit关联的OkHttp客户端,关闭其线程池
    val retrofit = RetrofitClient::class.java.getDeclaredField("instance").apply {
        isAccessible = true
    }.get(null) as Retrofit
    val okHttpClient = retrofit.callFactory() as OkHttpClient
    okHttpClient.dispatcher.executorService.shutdown()
}

注:这种方式需要做类型转换,适合快速测试场景,正式项目建议用方法1。

方法3:让OkHttp使用守护线程

配置OkHttp的Dispatcher使用守护线程,这样JVM退出时不会等待这些线程:

private val okHttpClient = OkHttpClient.Builder()
    .dispatcher(
        okhttp3.Dispatcher(Executors.newCachedThreadPool { r ->
            Thread(r).apply { isDaemon = true }
        })
    )
    .build()

这种方式下,只要runBlocking的协程作用域执行完毕,JVM就会直接退出,无需等待OkHttp的空闲线程。

总结

核心原因就是OkHttp的非守护空闲线程阻止了JVM退出,上面的几种方法都能解决问题:

  • 正式项目推荐用方法1,通过自定义OkHttp客户端调整超时时间,既规范又灵活;
  • 测试脚本可以用方法3,设置守护线程最省心;
  • 临时调试可以用方法2,手动关闭线程池快速见效。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 11:04:37