OkHttp是专为Android和Kotlin设计的高性能HTTP客户端,由Square公司开发,作为Retrofit和其他网络库的基础。它提供高效的连接管理、内置缓存和对HTTP/2的支持。根据Square, 2025的数据,OkHttp每天在全球应用程序中处理数十亿次请求。
要点
OkHttp是专为Java、Android和Kotlin设计的高效HTTP客户端,由Square公司开发。该库提供低级API来执行HTTP请求,支持HTTP/2、SPDY、WebSocket,并在网络故障时自动恢复连接。
OkHttp于2013年应运而生,旨在解决对可靠HTTP客户端的迫切需求,以解决HttpURLConnection的问题——缺乏连接池、对HTTP/2支持薄弱以及API不便。到2025年,OkHttp已在Android API系统级别使用:自Android 4.4(API 19)起,OkHttp已嵌入HttpURLConnection的实现中。
根据Google I/O 2024的数据,OkHttp处理了Android生态系统中超过70%的HTTP请求。这是因为OkHttp是Retrofit、Apollo GraphQL、Firebase和许多其他库的传输层。开发者无需显式连接即可自动获得OkHttp的功能。
OkHttp的架构建立在拦截器链(Interceptor chain)之上。每个请求都经过一系列拦截器,这些拦截器可以修改Request、Response或中断执行。这种架构类似于Chain of Responsibility模式,允许灵活扩展功能。
当应用程序发送请求时,OkHttp执行以下步骤:解析DNS、从池中选择连接(或创建新连接)、打开TLS握手(如果是HTTPS)、发送HTTP请求、接收响应并将其返回给应用程序。RealCall是一个内部类,管理从创建到完成的完整请求生命周期。
OkHttp自动处理重定向(302、301),在网络故障时重试请求(retry),遵循keep-alive协议,并支持透明的gzip压缩。开发者无需为这些操作编写代码——OkHttp会根据服务器标头自动执行这些操作。
HTTP/2允许通过单个TCP连接同时发送多个请求,无需阻塞(head-of-line blocking,这是HTTP/1.1的特性)。如果服务器支持此协议,OkHttp会自动使用HTTP/2,并在必要时透明地切换到HTTP/1.1。
HTTP/2多路复用对于移动应用程序尤其重要,因为建立连接(TCP + TLS)的延迟可能为100–300毫秒。OkHttp不是使用10个顺序连接,而是使用一个连接,在连接不稳定的典型Android设备上,总延迟可减少40–60%。
Interceptor是一个接口,只有一个方法intercept(Chain),它接收Request,执行操作并返回Response。拦截器有两种类型:应用程序拦截器(通过addInterceptor添加)和网络拦截器(addNetworkInterceptor)。
应用程序拦截器在HTTP请求形成之前触发——它们看到原始Request和所有转换后的最终Response。网络拦截器在网络级别触发:它们在gzip压缩、Content-Length标头添加、重定向和重试之后看到请求。如果响应来自缓存,则不会调用网络拦截器。
| 拦截器类型 | 添加方法 | 何时调用 | 是否看到缓存 |
|---|---|---|---|
| Application Interceptor | addInterceptor() | 请求之前和之后 | 是 |
| Network Interceptor | addNetworkInterceptor() | 在网络级别 | 否 |
在实践中,OkHttp拦截器解决三个主要任务:授权(添加Authorization标头)、日志记录(用于调试的HttpLoggingInterceptor)和重试(在网络故障时自动重试请求)。通过组合多个拦截器,可以构建完整的请求处理管道,而无需在每个HTTP调用中重复代码。
拦截器的添加顺序很重要:首先添加的Interceptor在入口处首先执行,在出口处最后执行。对于NetworkInterceptor,顺序由网络堆栈决定。推荐顺序:AuthInterceptor(添加令牌)、LoggingInterceptor(记录请求)、RetryInterceptor(在故障时重试)。
用于调试网络请求时使用HttpLoggingInterceptor——Square提供的现成拦截器。它记录方法、URL、标头以及请求和响应的正文。日志记录级别:BASIC(方法+URL+代码)、HEADERS(带标头)和BODY(完整请求和响应)。BODY在开发时很有用,但在生产环境中出于安全和性能原因会被禁用。
让我们看一下通过OkHttp的基本GET请求。首先创建OkHttpClient——一个重量级对象,创建一次后重复使用。然后使用URL形成Request,并通过execute同步执行请求或通过enqueue异步执行。
val client = OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.build()
val request = Request.Builder()
.url("https://api.github.com/users/octocat")
.header("Accept", "application/vnd.github.v3+json")
.build()
val response = client.newCall(request).execute()
println(response.body()?.string())
对于异步执行,使用接受Callback的enqueue方法。OkHttp在后台线程中执行请求,并在同一线程的callback中返回结果。要切换到Android主线程,请使用Handler或协程。
client.newCall(request).enqueue(object : Callback {
override fun onFailure(
call: Call, e: IOException
) {
println("请求失败:${e.message}")
}
override fun onResponse(
call: Call, response: Response
) {
println(response.body()?.string())
}
})
自定义Interceptor向每个请求添加Bearer令牌。拦截器检查Authorization标头的存在,如果令牌尚未设置,则从存储中添加它。在401响应时,拦截器可以通过Authenticator更新令牌。
class AuthInterceptor(
private val tokenProvider: () -> String?
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
val token = tokenProvider.invoke()
val request = originalRequest.newBuilder()
.header("Authorization", "Bearer $token")
.build()
return chain.proceed(request)
}
}
连接池(ConnectionPool)——OkHttp的关键优化,允许多个请求重用TCP连接。OkHttp不是为每个请求创建新的套接字,而是默认存储最多5个不活动连接,持续5分钟,这使对同一主机的重复请求延迟降低30–70%。
响应的缓存通过Cache类实现。要启用缓存,只需在OkHttpClient.Builder中指定目录和最大大小。OkHttp根据Cache-Control、Expires和ETag标头自动缓存GET响应,如果未过期,则返回缓存数据而无需网络请求。
val cacheDir = File(context.cacheDir, "http-cache")
val cache = Cache(cacheDir, 10L * 1024 * 1024)
val client = OkHttpClient.Builder()
.cache(cache)
.connectionPool(ConnectionPool(5, 5, TimeUnit.MINUTES))
.build()
正确配置池和缓存对于频繁请求的应用程序尤为重要——新闻提要、聊天、数据更新。如果没有池,每个TCP连接都需要三次握手(SYN、SYN-ACK、ACK)以及可能的TLS握手(2–3轮往返),这会给每个请求增加100–500毫秒的延迟。
OkHttp还通过RealWebSocket类支持WebSocket。WebSocket连接通过HTTP握手(101 Switching Protocols)建立,然后切换到双向协议。OkHttp自动发送ping帧以保持连接活动,并在断开时重新连接。OkHttp的WebSocket与wss://echo.websocket.org等标准端点兼容。
为每个请求创建OkHttpClient——最常见的错误。OkHttpClient包含连接池、缓存和线程池。为每个请求创建新实例不仅浪费内存,还会失去连接重用的优势。OkHttpClient应该通过DI容器作为单例。
忽略关闭Response.body()会导致资源泄漏。ResponseBody包含一个InputStream,在读取后必须关闭。如果使用body().string()或body().bytes(),OkHttp会自动关闭流,但在读取body().byteStream()或body().charStream()时,需要在finally块中显式调用close()。
缺少超时处理——另一个问题。默认情况下,OkHttp没有超时设置(connectTimeout = 10秒,readTimeout = 10秒,writeTimeout = 10秒)。对于连接不稳定的移动应用程序,建议将connectTimeout设置为15–30秒,readTimeout设置为15–30秒,否则用户在信号弱时会等待过长时间。
常见问题
OkHttp是低级HTTP客户端,手动管理Request和Response。Retrofit是带有注释的高级包装器。OkHttp用作Retrofit的传输层,但也可以独立工作,无需额外的库。
OkHttp使用SSLSocketFactory进行TLS握手。该库支持CertificatePinner用于证书固定(Certificate Pinning)、TrustManager用于自定义验证以及HostnameVerifier用于根据证书检查主机名。
同步请求在网络问题时抛出IOException。异步请求收到带有IOException的onFailure回调。对于HTTP错误(4xx、5xx),响应被视为成功——错误代码通过response.isSuccessful()检查。
是的,OkHttp通过WebSocket类和WebSocketListener内置了对WebSocket的支持。建立连接后,WebSocket允许实时发送和接收消息,无需重复的HTTP请求。
通过OkHttpClient.Builder中的followRedirects(false)和followSslRedirects(false)禁用自动重定向。当需要手动处理重定向时,例如从重定向URL中提取令牌,这很有用。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。