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

为何Kotlin/Retrofit脚本执行完毕后无法正常退出?

为什么这段Kotlin/Retrofit脚本无法正常退出?

这段改编自Retrofit官方示例的Kotlin脚本,在执行完打印操作后会挂起,必须手动调用exitProcess(0)才能退出,核心原因和OkHttp的后台线程机制有关:

问题代码

#!/usr/bin/env kotlin

@file:Repository("https://repo.maven.apache.org/maven2/")
@file:DependsOn("com.squareup.retrofit2:retrofit:2.9.0")
@file:DependsOn("com.squareup.retrofit2:converter-gson:2.9.0")

import retrofit2.Call
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.GET
import retrofit2.http.Path
import java.lang.RuntimeException
import kotlin.system.exitProcess

data class Repo(
    val id: Int,
    val name: String
)

interface GitHubService {
    @GET("users/{user}/repos")
    fun listRepos(@Path("user") user: String?): Call<List<Repo>?>?
}

var retrofit: Retrofit = Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .build()

var service: GitHubService = retrofit.create(GitHubService::class.java) ?: throw RuntimeException()

val repoList = service.listRepos("octocat")?.execute()?.body() ?: throw RuntimeException()

println("The repos' names: ${repoList.joinToString(", ") { it.name }} ")

// The program just hangs unless I uncomment the next line
//exitProcess(0)

挂起原因

Retrofit默认依赖OkHttp作为底层HTTP客户端,OkHttp内部的Dispatcher(请求调度器)会维护一个非守护线程池,用来处理请求执行和连接池的后台维护(比如清理空闲连接)。

当主线程执行完所有业务代码后,这些后台线程仍然处于活跃状态(等待接收新请求或执行定时任务)。JVM的退出规则是:只有当所有非守护线程都终止时,JVM才会停止运行。因此这些未终止的后台线程会阻止JVM正常退出,导致脚本挂起。

解决方法

方法1:手动强制退出JVM

直接调用exitProcess(0),这是最直接的方式,会立即终止整个JVM进程,不受后台线程状态影响。

方法2:显式关闭OkHttp客户端

通过手动创建并持有OkHttpClient实例,使用完毕后关闭它,让后台线程池终止:

// 新增OkHttpClient实例创建
val okHttpClient = OkHttpClient.Builder().build()

// Retrofit构建时指定自定义的OkHttpClient
var retrofit: Retrofit = Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .client(okHttpClient) // 添加这一行
    .addConverterFactory(GsonConverterFactory.create())
    .build()

// ... 原有业务代码 ...

println("The repos' names: ${repoList.joinToString(", ") { it.name }} ")

// 关闭OkHttpClient,终止后台线程
okHttpClient.close()

调用close()会自动关闭Dispatcher的线程池和连接池,让所有后台线程终止,JVM就能正常退出了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 15:22:58