okhttp源码解析

发布于:2025-03-11 ⋅ 阅读:(14) ⋅ 点赞:(0)

1、okhttp比httpurlconnection好在哪里

OkHttp 相比于 HttpURLConnection 有以下优势:

功能丰富

支持连接池:OkHttp 通过管理连接池可以复用连接,减少了请求延时。而 HttpURLConnection 每次请求都需要重新建立连接,效率降低。

支持缓存:OkHttp 支持透明的 GZIP 压缩和响应缓存,能够减少数据量和避免重复网络请求,节省带宽和提高性能。

支持 HTTP/2 协议:OkHttp 支持 HTTP/2 协议,可提高效率和速度。

安全特性:除了支持SSL证书还支持现代TLS,如 TLS 1.3、ALPN、证书锁定等,增强了安全性。

自动恢复连接:在网络不稳定请求失败时,OkHttp 能自动恢复连接,提高了网络请求的稳定性。

DNS 扩展支持:提供了 DNS 扩展支持,增强了网络请求的灵活性。

支持WebSocket 协议: WebSocket 协议,适用于实时通信场景,服务器侧可以直接调用客户端。

性能优越

减少请求延迟:通过上述提到连接池复用技术,降低了网络连接的开销和时间,尤其在处理大数据量或频繁的网络请求时性能通常优于 HttpURLConnection。

自动处理常见网络问题:如二次连接、SSL 的握手问题等,减少了开发者在处理网络问题上的工作量。

易用性高

API 设计简洁明了:功能丰富使用简单,支持同步阻塞调用和异步回调调用,Call接口的两种方式如下:    

Response execute() throws IOException;

    void enqueue(Callback responseCallback);

扩展性强:提供了丰富的扩展点,允许开发者根据自己的需求进行定制和扩展,例如通过实现 Interceptor 接口来拦截和修改请求和响应,还可以通过OKhttpClient的cookieJar设置Cookie。

兼容性好

跨平台稳定:在不同的平台上具有良好的兼容性和稳定性,不用担心 Android 版本变换的困扰。

支持多种版本:支持 Android 2.3 及其以上版本,支持 Java JDK 1.7 以上版本。

2、对Android中OkHttp源码的解析

2.1核心类

2.1.1 OkHttpClient

OkHttpClient是OkHttp的核心类,负责配置和创建HTTP请求。它的构造函数允许设置各种参数,如超时时间、拦截器、连接池、Cookie等。

OkHttpClient 使用了建造者模式,有一个内部类Builder。上面介绍的这些参数可以通过OkHttpClient对象直接设置,也可以先构建OkHttpClien.Buildert对象然后设置参数最后调用他的build()方法创建OkHttpClien对象并统一设置参数。

public OkHttpClient build() {
    return new OkHttpClient(this);
}

2.1.2 Request 和 Response

Request和Response类分别表示HTTP请求和响应。它们包含了请求的URL、方法、头部信息和返回报文信息等。都是final的类不可被继承。

public final class Request {

}

public final class Response {

}

2.1.3 Call

Call是一个接口,表示一个HTTP请求的执行。它提供了同步和异步执行请求的方法。是不是很熟悉,我们就是在这两个方法接受返回的成功和失败的报文。

public interface Call {

    Response execute() throws IOException;

    void enqueue(Callback responseCallback);

}

注意:Call只是一个接口,实际业务都是在的实现类RealCall类中完成。

2.1.4 Interceptor 拦截器

OkHttp支持拦截器,可以在请求发送前和响应接收后进行处理。拦截器可以用于添加公共头部、日志记录、重试等。

public interface Interceptor {

    Response intercept(Chain chain) throws IOException;

    interface Chain {

        Request request();

        Response proceed(Request request) throws IOException;

    }

}

3、请求网络的基本流程

同步请求

1. 创建OkHttpClient对象,可以通过new OkHttpClient.Builder().build()的方式配置一些参数。

2. 创建Request对象,通过Builder模式生成,其中可以配置与请求相关的参数,如URL等。

3. 通过client.newCall(request).execute(发送请求)。具体执行逻辑为:

    - 将对应任务加入分发器。

    - 执行任务,调用`getResponseWithInterceptorChain()`方法,该方法会依次执行拦截器链。

    - 执行完成后通知dispatcher对应任务已完成,对应任务出队。

异步请求

异步请求的基本流程与同步请求类似,只是在发送请求时调用client.newCall(request).enqueue(new Callback() {...}),并设定一个回调对象`Callback`来处理请求成功或失败的情况。

关键组件的工作原理

Dispatcher分发器

Dispatcher分发器类似于Nginx中的反向代理,通过Dispatcher将任务分发到合适的空闲线程,实现非阻塞、高可用、高并发连接。在OkHttp中,构建了一个阀值为[0, Integer.MAX_VALUE]的线程池,它不保留任何最小线程数,随时创建更多的线程数,当线程空闲时只能活60秒,它使用了一个不存储元素的阻塞工作队列,一个叫做OkHttp Dispatcher的线程工厂。

详见下文“4、okhttp的责任链模式解析

缓存机制

OkHttp的缓存机制主要通过CacheInterceptor拦截器实现。当发起请求时,CacheInterceptor会先检查缓存,如果存在有效的缓存响应,则直接返回缓存响应。如果不存在有效的缓存响应,则会继续执行下一个拦截器,即发起网络请求。在网络请求返回后,根据响应头中的缓存相关字段,决定是否将响应缓存起来。例如,如果响应头中包含`Cache-Control: max-age=3600`,则表示该响应可以被缓存1小时。

连接池

OkHttp的连接池通过`ConnectionPool`类实现,内部通过一个双端队列(dequeue)来维护当前所有连接,主要涉及到的操作包括:put放入新连接、get从连接池中获取连接、evictAll关闭所有连接、connectionBecameIdle连接变空闲后调用清理线程、deduplicate清除重复的多路复用线程。`StreamAllocation`在其`findConnection`方法内部通过调用`ConnectionPool`的get方法为其找到stream找到合适的连接,如果没有则新建一个连接。

4、okhttp的责任链模式解析

OkHttp 通过责任链模式来处理 HTTP 请求,这种模式允许将请求沿着处理者链发送,每个处理者均可对请求进行处理,或将其传递给链上的下一个处理者。以下是 OkHttp 中责任链模式的具体解析:

4.1 拦截器的分类

OkHttp 中的拦截器有多个,可以分为两类:

1、应用程序拦截器:这些拦截器在请求开始之前最早被调用,适用于对请求进行预处理,如添加公共头部、日志记录等。它们总是被调用一次,即使 HTTP 响应是从缓存中提供的。

2网络拦截器:这些拦截器在真正发起请求之前被调用,适用于对网络请求进行处理,如重试、重定向等。它们只有在非缓存响应时才会被调用。

4.2. 拦截器的串联

OkHttp 内置了多个核心拦截器,这些拦截器通过责任链模式串联起来,使得请求可以在不同拦截器之间流转和处理。责任链的入口从第一个 RealInterceptorChain对象的 proceed 方法调用开始。

4.3. 拦截器的执行流程

当调用 client.newCall(request).execute() 或 client.newCall(request).enqueue(callback) 时,OkHttp 会构建一个完整的拦截器链,并通过 proceed 方法依次调用每个拦截器。具体步骤如下:

1. 构建拦截器链,多个:   

List<Interceptor> interceptors = new ArrayList<>();

   interceptors.addAll(client.interceptors());

   interceptors.add(new RetryAndFollowUpInterceptor(client, forWebSocket));

   interceptors.add(new BridgeInterceptor(client.cookieJar()));

   interceptors.add(new CacheInterceptor(client.cache()));

   interceptors.add(new ConnectInterceptor());

   if (!forWebSocket) {

       interceptors.addAll(client.networkInterceptors());

   }

   interceptors.add(new CallServerInterceptor(forWebSocket));

   RealInterceptorChain chain = new RealInterceptorChain(interceptors, null, null, 0, originalRequest, this, eventListener, client.connectTimeoutMillis(), client.readTimeoutMillis(), client.writeTimeoutMillis());

2. 执行拦截器链:

   Response response = chain.proceed(originalRequest);

3. 拦截器的 proceed 方法:   

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection) throws IOException {

       if (index >= interceptors.size()) throw new AssertionError();

       calls++;

       // 创建新的拦截链,链中的拦截器集合 index+1

       RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec, connection, index + 1, request, call, eventListener, connectTimeout, readTimeout, writeTimeout);

       // 执行当前的拦截器

       Interceptor interceptor = interceptors.get(index);

       Response response = interceptor.intercept(next);

       return response;

   }

4.4 拦截器的具体实现

RetryAndFollowUpInterceptor:负责重试和重定向。如果请求失败,会尝试重试;如果响应是重定向,会继续请求新的 URL。

BridgeInterceptor:桥接应用层和网络层,添加必要的请求头信息,如 `Content-Encoding`、`Cookie`、`User-Agent` 等。

CacheInterceptor:负责缓存处理。如果缓存有效,直接返回缓存响应;否则,继续执行下一个拦截器。

ConnectInterceptor:负责建立网络连接。

CallServerInterceptor:负责发送网络请求和读取网络响应。这是责任链的最后一个拦截器,实际执行网络 I/O 操作。

4.5 拦截器优点

控制请求处理的顺序:可以明确每个拦截器的执行顺序。

解耦:发起操作和执行操作的类解耦,每个拦截器只负责自己的任务。

扩展性:可以在不更改现有代码的情况下新增处理者。

通过责任链模式,OkHttp 将复杂的请求处理逻辑分解为多个小的、可管理的拦截器,每个拦截器只负责一部分任务,使得代码更加清晰和易于维护。

5、okhttp发起请求到收到响应完整流程

OkHttp 发起请求到收到响应的完整流程涉及多个关键步骤和组件:

5.1 创建 OkHttpClient 和 Request

首先,需要创建一个 `OkHttpClient` 实例和一个 `Request` 实例。`OkHttpClient` 用于配置客户端的各种参数,如超时、拦截器、连接池等。`Request` 用于定义具体的 HTTP 请求,包括 URL、方法、头部和请求体等。

5.2 创建 Call 对象

通过 `OkHttpClient` 的 `newCall` 方法创建一个 `Call` 对象。`Call` 对象表示一个具体的 HTTP 请求的执行。

Call call = client.newCall(request);

5.3. 发起请求

可以使用同步或异步方式发起请求。

5.3.1 同步请求

同步请求会阻塞当前线程,直到请求完成并返回响应。

Response response = call.execute();

5.3.2 异步请求

异步请求不会阻塞当前线程,而是通过回调来处理响应。

call.enqueue(new Callback() {

    @Override

    public void onFailure(Call call, IOException e) {

        // 请求失败处理

    }



    @Override

    public void onResponse(Call call, Response response) throws IOException {

        // 请求成功处理

    }

});

 5.4. 构建拦截器链

OkHttp 内部会构建一个拦截器链,拦截器链的入口是 `RealInterceptorChain` 对象的 `proceed` 方法。拦截器链的构建过程如下:

List<Interceptor> interceptors = new ArrayList<>();

interceptors.addAll(client.interceptors());

interceptors.add(new RetryAndFollowUpInterceptor(client, forWebSocket));

interceptors.add(new BridgeInterceptor(client.cookieJar()));

interceptors.add(new CacheInterceptor(client.cache()));

interceptors.add(new ConnectInterceptor());

if (!forWebSocket) {

    interceptors.addAll(client.networkInterceptors());

}

interceptors.add(new CallServerInterceptor(forWebSocket));

RealInterceptorChain chain = new RealInterceptorChain(interceptors, null, null, 0, originalRequest, this, eventListener, client.connectTimeoutMillis(), client.readTimeoutMillis(), client.writeTimeoutMillis());

5.5. 执行拦截器链

通过 chain.proceed(request) 方法依次调用每个拦截器。每个拦截器的 `intercept` 方法都会调用 `proceed` 方法来传递请求到下一个拦截器。

Response response = chain.proceed(request);

5.6. 拦截器的具体执行

应用程序拦截器

RetryAndFollowUpInterceptor:负责重试和重定向。如果请求失败,会尝试重试;如果响应是重定向,会继续请求新的 URL。

BridgeInterceptor:桥接应用层和网络层,添加必要的请求头信息,如 `Content-Encoding`、`Cookie`、`User-Agent` 等。

CacheInterceptor:负责缓存处理。如果缓存有效,直接返回缓存响应;否则,继续执行下一个拦截器。

网络拦截器

ConnectInterceptor:负责建立网络连接。

其他网络拦截器:如 `LoggingInterceptor`(用于日志记录)等。

服务器拦截器

CallServerInterceptor:负责发送网络请求和读取网络响应。这是责任链的最后一个拦截器,实际执行网络 I/O 操作。

5.7. 进行网络请求

CallServerInterceptor 会创建一个 `HttpCodec` 对象,用于发送请求和读取响应。具体步骤如下:

5.7.1 创建连接:

通过 `ConnectionPool` 获取或创建一个连接。

5.7.2. 发送请求:

将请求头和请求体发送到服务器。

5.7.3. 读取响应:

读取服务器返回的响应头和响应体。

5.8. 返回响应

最终,响应会通过拦截器链逐层返回,直到到达最外层的 `Call` 对象。对于同步请求,响应会直接返回给调用者;对于异步请求,响应会通过回调方法返回。

`CallServerInterceptor` 通过 `httpCodec.readResponseHeaders()` 读取响应头,并构建 `Response` 对象。如果响应体存在,还会通过 `httpCodec.openResponseBody(response)` 打开响应体流。

Response response = httpCodec.readResponseHeaders()

        .request(request)

        .handshake(connection.handshake())

        .sentRequestAtMillis(sentRequestMillis)

        .receivedResponseAtMillis(System.currentTimeMillis())

        .build();



if (!forWebSocket && code == 200 && ("HEAD".equalsIgnoreCase(request.method()) || "GET".equalsIgnoreCase(request.method()))) {

    response = response.newBuilder()

            .body(httpCodec.openResponseBody(response))

            .build();

}



return response;

Response 对象通过责任链逐层返回,直到到达最外层的 Call 对象。对于同步请求,Response 对象直接返回给调用者;对于异步请求,Response 对象通过回调方法返回。

5.9. 关闭资源

在处理完响应后,需要关闭响应体和连接,以释放资源。

if (response.isSuccessful()) {

    // 处理响应体

    String responseBody = response.body().string();

} else {

    // 处理错误

}

response.close();

完整流程如下:

① [创建 OkHttpClient 和 Request]

② [创建 Call 对象]

    ③ [发起同步请求或异步请求]

    ④ [构建拦截器链]

    ⑤ [执行拦截器链]

    ⑥ [应用程序拦截器]

    ⑦ [网络拦截器]

    ⑧ [服务器拦截器]

    ⑨ [发送网络请求]

    ⑩ [读取网络响应]

    11、[返回响应]

    12、[关闭资源]

以上是OkHttp处理 HTTP 请求和响应流程。