优点
OkHttp是一个高效的Http客户端,有如下的特点:
- 支持HTTP2/SPDY黑科技
- socket自动选择最好路线,并支持自动重连
- 拥有自动维护的socket连接池,减少握手次数
- 拥有队列线程池,轻松写并发
- 拥有Interceptors轻松处理请求与响应(比如透明GZIP压缩,LOGGING)
- 基于Headers的缓存策略(不仅可以缓存数据,就连响应头都给缓存了)
概念
源码涉及的主要几个对象
- Call:对请求的封装,有异步请求和同步请求。
- Dispatcher:任务调度器
- Connection:是RealConnection的父类接口,表示对JDK中的物理socket进行了引用计数封装,用来控制socket连接
- HttpCodec:对Http请求进行编码,对Http响应进行解码,由于Http协议有基于HTTP1.0和- Http2.0的两种情况,Http1Code代表基于Http1.0协议的方式,Http2Code代表基于Http2.0协议的方式。
- StreamAllocation: 用来控制Connections/Streams的资源分配与释放
- RouteDatabase:用来保存连接的错误路径,以便能提升连接的效率。
- RetryAndFollowUpInterceptor 负责失败重试以及重定向的拦截器
- BridgeInterceptor: 负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转为用户友好的响应的
- CacheInterceptor: 负责读取缓存直接返回、更新缓存
- ConnectInterceptor: 负责和服务器建立连接的
- CallServerInterceptor:负责向服务器发送请求数据、从服务器读取响应数据
源码
我们先从使用方法开始慢慢深入源码阶段
OkHttpClient mOkHttpClient = new OkHttpClient();
final Request request = new Request.Builder()
.url("https://blog.csdn.net/dakun012")
.addHeader("user_agent","00000")
.build();
Call call = mOkHttpClient.newCall(request);//1
call.enqueue(new Callback() {//2
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
Call
我们来看下注释1处都做了些什么
/**
* Prepares the {@code request} to be executed at some point in the future.
*/
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
接着调用了RealCall.newRealCall()方法
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
// Safely publish the Call instance to the EventListener.
RealCall call = new RealCall(client, originalRequest, forWebSocket);
call.eventListener = client.eventListenerFactory().create(call);
return call;
}
我们看到实际上返回的是RealCall,RealCall构造方法如下:
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
}
构造方法中只是简单的赋值操作和实例化了RetryAndFollowUpInterceptor拦截器,这个稍后会说,我们返回去看注释2,上面我们看到call其实际类型是RealCall,所以去看下RealCall.enqueue都做了什么
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
client.dispatcher()返回的是Dispatcher类型
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
如果正在运行的异步请求数量小于最大的并发数,且正在运行的每个主机请求数量小于规定的每个主机最大请求数量,那么就把该请求放进正在运行的异步请求队列中,否则就把该请求放进将要执行的异步请求队列中。
Dispatcher
主要参数介绍
//支持的最大并发请求数量
private int maxRequests = 64;
//每个主机的最大请求数量
private int maxRequestsPerHost = 5;
//请求线程池
private @Nullable ExecutorService executorService;
//将要运行的异步请求队列
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
//正在运行的异步请求队列
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
//正在运行的同步请求队列
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
继续看看Dispatcher的构造方法和executorService方法,如下:
public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}
public Dispatcher() {
}
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
我们看到线程池是可以自定义的,只要在构造OkHttpClient 时传入自定义Dispatcher即可
我们重点看下executorService
Dispatcher初始化了一个线程池,核心线程的数量为0,最大的线程数量为Integer.MAX_VALUE,空闲线程存在的最大时间为60秒,这个线程类似于CacheThreadPool,比较适合执行大量的耗时比较少的任务。同时我们Dispatcher也可以来设置自己线程池。
了解了这些概念后,我们回到executorService().execute(call)方法继续看
call的类型是AsyncCall继承自NamedRunnable,NamedRunnable继承自Runnable,上面的execute(call)方法自然是执行的Runnable.run()方法,NamedRunnable实现了run方法
@Override public final void run() {
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName(name);
try {
execute();
} finally {
Thread.currentThread().setName(oldName);
}
}
protected abstract void execute();
所以最终会调用AsyncCall.execute();
@Override protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
RealCall通过执行getResponseWithInterceptorChain()返回Response,如果请求被取消或发生异常则在进行OnFailue回调,如果请求成功则进行onResponse的回调。
这里要注意两点:
- 请求如果被取消,其回调实在onFailue中进行回调的
- enqueue方法的回调是在子线程中完成的
拦截器
AsyncCall是RealCall 内部类,AsyncCall调用的是RealCall.getResponseWithInterceptorChain(),那么RealCall 的getResponseWithInterceptorChain方法中究竟干了些什么呢,它是如何返回Response的呢?
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
interceptors 依次添加了
- client.interceptors() 在配置 OkHttpClient 时设置的 interceptors ()
- RetryAndFollowUpInterceptor 负责失败重试以及重定向的
- BridgeInterceptor 负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转为用户友好的响应的
- CacheInterceptor 负责读取缓存直接返回、更新缓存的
- ConnectInterceptor 负责和服务器建立连接的
- networkInterceptors 配置 OkHttpClient 时设置的
- CallServerInterceptor 负责向服务器发送请求数据、从服务器读取响应数据的
在 return chain.proceed(originalRequest),中开启链式调用
RealInterceptorChain的proceed方法源码如下:
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
//省略部分代码
// Call the next interceptor in the chain.
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;
}
这段猛一看有点头大,其实这个有点类似迭代调用的意思,盗个别人的图看下哈哈~
首先了解下拦截器,拦截器是一种能够监控、重写,重试调用的机制。通常情况下,拦截器用来添加、移除、转换请求和响应的头部信息。比如将域名替换为IP地址,在请求头中移除添加host属性;也可以添加我们应用中的一些公共参数,比如设备id、版本号,等等。
拦截器的基本代码结构如下:
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
interface Chain {
Request request();
Response proceed(Request request) throws IOException;
}
}
具体流程我们着重看下如下两个拦截器来梳理我们的思路
- BridgeInterceptor
- CacheInterceptor
我们假设第一个拦截器是BridgeInterceptor, Response response = interceptor.intercept(next);调用的就是BridgeInterceptor.intercept方法
@Override public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
//省略......操作原Request
Response networkResponse = chain.proceed(requestBuilder.build());
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
//省略......操作得到的Response
Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);
return responseBuilder.build();
}
我们看到在对原Request 进行一系列的修改操作后,又调用了chain.proceed(requestBuilder.build()),上面我们看了相应源码,其结果会再次调用下一个拦截器,如此一致循环调用,直到最后一个CallServerInterceptor拦截器的intercept调用后,得到Response 开始一级一级返回给上层拦截器
CallServerInterceptor.intercept源码如下:
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
HttpCodec httpCodec = realChain.httpStream();
StreamAllocation streamAllocation = realChain.streamAllocation();
RealConnection connection = (RealConnection) realChain.connection();
Request request = realChain.request();
long sentRequestMillis = System.currentTimeMillis();
realChain.eventListener().requestHeadersStart(realChain.call());
httpCodec.writeRequestHeaders(request);
realChain.eventListener().requestHeadersEnd(realChain.call(), request);
Response.Builder responseBuilder = null;
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
// If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
// Continue" response before transmitting the request body. If we don't get that, return
// what we did get (such as a 4xx response) without ever transmitting the request body.
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
httpCodec.flushRequest();
realChain.eventListener().responseHeadersStart(realChain.call());
responseBuilder = httpCodec.readResponseHeaders(true);
}
if (responseBuilder == null) {
// Write the request body if the "Expect: 100-continue" expectation was met.
realChain.eventListener().requestBodyStart(realChain.call());
long contentLength = request.body().contentLength();
CountingSink requestBodyOut =
new CountingSink(httpCodec.createRequestBody(request, contentLength));
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
realChain.eventListener()
.requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
} else if (!connection.isMultiplexed()) {
// If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
// from being reused. Otherwise we're still obligated to transmit the request body to
// leave the connection in a consistent state.
streamAllocation.noNewStreams();
}
}
httpCodec.finishRequest();
if (responseBuilder == null) {
realChain.eventListener().responseHeadersStart(realChain.call());
responseBuilder = httpCodec.readResponseHeaders(false);
}
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
realChain.eventListener()
.responseHeadersEnd(realChain.call(), response);
int code = response.code();
if (forWebSocket && code == 101) {
// Connection is upgrading, but we need to ensure interceptors see a non-null response body.
response = response.newBuilder()
.body(Util.EMPTY_RESPONSE)
.build();
} else {
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
}
if ("close".equalsIgnoreCase(response.request().header("Connection"))
|| "close".equalsIgnoreCase(response.header("Connection"))) {
streamAllocation.noNewStreams();
}
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
"HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
return response;
}
流程总结如下
我们Request 是经过拦截器一层层修改后发送给服务端的(a->b->c)
我们得到的response,也是拦截器一层层修改后返回给我们的(c->b->a)
拦截器详解
未完待续
本文章参考其他文章,加上自己的理解和认为难点的地方修改,如有侵权请联系我修改,再次感谢