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 请求和响应流程。