okhttp源码解析

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对象并统一设置参数。

java 复制代码
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请求的执行。它提供了同步和异步执行请求的方法。是不是很熟悉,我们就是在这两个方法接受返回的成功和失败的报文。

java 复制代码
public interface Call {

    Response execute() throws IOException;

    void enqueue(Callback responseCallback);

}

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

2.1.4 Interceptor 拦截器

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

java 复制代码
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. 构建拦截器链,多个:
java 复制代码
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());
  1. 执行拦截器链:

Response response = chain.proceed(originalRequest);

  1. 拦截器的 proceed 方法:
java 复制代码
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 异步请求

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

java 复制代码
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` 方法。拦截器链的构建过程如下:

java 复制代码
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)` 打开响应体流。

java 复制代码
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. 关闭资源

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

java 复制代码
if (response.isSuccessful()) {

    // 处理响应体

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

} else {

    // 处理错误

}

response.close();

完整流程如下:

① [创建 OkHttpClient 和 Request]

② [创建 Call 对象]

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

④ [构建拦截器链]

⑤ [执行拦截器链]

⑥ [应用程序拦截器]

⑦ [网络拦截器]

⑧ [服务器拦截器]

⑨ [发送网络请求]

⑩ [读取网络响应]

11、[返回响应]

12、[关闭资源]

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

相关推荐
张拭心11 分钟前
春节后,有些公司明确要求 AI 经验了
android·前端·人工智能
张拭心21 分钟前
Android 17 来了!新特性介绍与适配建议
android·前端
Kapaseker3 小时前
Compose 进阶—巧用 GraphicsLayer
android·kotlin
黄林晴3 小时前
Android17 为什么重写 MessageQueue
android
阿巴斯甜1 天前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker1 天前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95271 天前
Andorid Google 登录接入文档
android
黄林晴1 天前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab2 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿2 天前
Android MediaPlayer 笔记
android