OkHttp — 什么是它,功能及HTTP客户端架构

作者: IT Sectr 发布日期: 2026-03-07 阅读时间: 8 分钟

OkHttp是专为Android和Kotlin设计的高性能HTTP客户端,由Square公司开发,作为Retrofit和其他网络库的基础。它提供高效的连接管理、内置缓存和对HTTP/2的支持。根据Square, 2025的数据,OkHttp每天在全球应用程序中处理数十亿次请求。

要点

  • OkHttp — Square提供的支持HTTP/2和SPDY的Android和Kotlin HTTP客户端
  • 连接池 — 重用TCP连接以减少延迟的机制
  • 拦截器 Interceptor和NetworkInterceptor修改请求和响应
  • 缓存 — 内置Cache减少重复请求的流量
  • WebSocket — 通过WebSocket协议支持双向通信

什么是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的工作原理

OkHttp的架构建立在拦截器链(Interceptor chain)之上。每个请求都经过一系列拦截器,这些拦截器可以修改Request、Response或中断执行。这种架构类似于Chain of Responsibility模式,允许灵活扩展功能。

当应用程序发送请求时,OkHttp执行以下步骤:解析DNS、从池中选择连接(或创建新连接)、打开TLS握手(如果是HTTPS)、发送HTTP请求、接收响应并将其返回给应用程序。RealCall是一个内部类,管理从创建到完成的完整请求生命周期。

OkHttp自动处理重定向(302、301),在网络故障时重试请求(retry),遵循keep-alive协议,并支持透明的gzip压缩。开发者无需为这些操作编写代码——OkHttp会根据服务器标头自动执行这些操作。

HTTP/2支持和多路复用

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%

OkHttp拦截器:Interceptor和NetworkInterceptor

Interceptor是一个接口,只有一个方法intercept(Chain),它接收Request,执行操作并返回Response。拦截器有两种类型:应用程序拦截器(通过addInterceptor添加)和网络拦截器(addNetworkInterceptor)。

应用程序拦截器在HTTP请求形成之前触发——它们看到原始Request和所有转换后的最终Response。网络拦截器在网络级别触发:它们在gzip压缩、Content-Length标头添加、重定向和重试之后看到请求。如果响应来自缓存,则不会调用网络拦截器。

拦截器类型添加方法何时调用是否看到缓存
Application InterceptoraddInterceptor()请求之前和之后
Network InterceptoraddNetworkInterceptor()在网络级别

拦截器的实际应用

在实践中,OkHttp拦截器解决三个主要任务:授权(添加Authorization标头)、日志记录(用于调试的HttpLoggingInterceptor)和重试(在网络故障时自动重试请求)。通过组合多个拦截器,可以构建完整的请求处理管道,而无需在每个HTTP调用中重复代码。

拦截器的添加顺序很重要:首先添加的Interceptor在入口处首先执行,在出口处最后执行。对于NetworkInterceptor,顺序由网络堆栈决定。推荐顺序:AuthInterceptor(添加令牌)、LoggingInterceptor(记录请求)、RetryInterceptor(在故障时重试)。

通过HttpLoggingInterceptor记录日志

用于调试网络请求时使用HttpLoggingInterceptor——Square提供的现成拦截器。它记录方法、URL、标头以及请求和响应的正文。日志记录级别:BASIC(方法+URL+代码)、HEADERS(带标头)和BODY(完整请求和响应)。BODY在开发时很有用,但在生产环境中出于安全和性能原因会被禁用。

Kotlin中的OkHttp代码示例

让我们看一下通过OkHttp的基本GET请求。首先创建OkHttpClient——一个重量级对象,创建一次后重复使用。然后使用URL形成Request,并通过execute同步执行请求或通过enqueue异步执行。

kotlin
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或协程。

kotlin
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更新令牌。

kotlin
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)
    }
}

OkHttp连接池和缓存

连接池(ConnectionPool)——OkHttp的关键优化,允许多个请求重用TCP连接。OkHttp不是为每个请求创建新的套接字,而是默认存储最多5个不活动连接,持续5分钟,这使对同一主机的重复请求延迟降低30–70%

响应的缓存通过Cache类实现。要启用缓存,只需在OkHttpClient.Builder中指定目录和最大大小。OkHttp根据Cache-Control、Expires和ETag标头自动缓存GET响应,如果未过期,则返回缓存数据而无需网络请求。

kotlin
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等标准端点兼容。

使用OkHttp时的常见错误

为每个请求创建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与Retrofit有什么区别?

OkHttp是低级HTTP客户端,手动管理Request和Response。Retrofit是带有注释的高级包装器。OkHttp用作Retrofit的传输层,但也可以独立工作,无需额外的库。

OkHttp如何处理HTTPS?

OkHttp使用SSLSocketFactory进行TLS握手。该库支持CertificatePinner用于证书固定(Certificate Pinning)、TrustManager用于自定义验证以及HostnameVerifier用于根据证书检查主机名。

如何在OkHttp中捕获网络错误?

同步请求在网络问题时抛出IOException。异步请求收到带有IOException的onFailure回调。对于HTTP错误(4xx、5xx),响应被视为成功——错误代码通过response.isSuccessful()检查。

OkHttp是否支持WebSocket?

是的,OkHttp通过WebSocket类和WebSocketListener内置了对WebSocket的支持。建立连接后,WebSocket允许实时发送和接收消息,无需重复的HTTP请求。

如何在OkHttp中禁用重定向?

通过OkHttpClient.Builder中的followRedirects(false)followSslRedirects(false)禁用自动重定向。当需要手动处理重定向时,例如从重定向URL中提取令牌,这很有用。

总结

  • OkHttp — Square提供的高性能HTTP客户端,支持HTTP/2和SPDY
  • 拦截器架构实现Chain of Responsibility模式以修改请求
  • 连接池重用TCP连接,将延迟降低30–70%
  • 缓存Cache-Control和ETag减少重复请求的流量
  • WebSocket提供双向实时通信
  • OkHttpClient应该是单例——为每个请求创建会导致泄漏
  • ResponseBody在流式读取byteStream时需要显式关闭

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读