Okhttp3 使用
get请求
1.拿到OkhttpClient对象
OkHttpClient client = new OkHttpClient();
2.构造Request对象
Request request = new Request.Builder().url(url).get().build();
3.将request封装为call
Call call = client.newCall(request);
4.根据需求使用同步或者异步方法
//同步调用
try {
Response response = call.execute();
} catch (IOException e) {
e.printStackTrace();
}
//异步调用,并设置回调
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//请求失败
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//请求成功,注意当前线程是子线程,
}
});
Post 请求
1.拿到OkhttpClient对象
OkHttpClient client = new OkHttpClient();
2.构建FormBody对象
FormBody formBody = new FormBody.Builder()
.add("key1", "value1")
.build();
3.构建Request对象
Request request = new Builder().url(url).post(formBody).build();
4.根据需求使用同步或者异步方法
//同步调用
try {
Response response = call.execute();
} catch (IOException e) {
e.printStackTrace();
}
//异步调用,并设置回调
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//请求失败
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//请求成功,注意当前线程是子线程,
}
});
Okhttp 流程分析
下图为Okhttp工作的大致流程
Call&& RealCall
1.首先看下这段代码:Call call = client.newCall(request);
OkhttpClient.java
@Override
public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
2.这里创建了一个RealCall的对象,而RealCall是实现了Call的接口。
Call.java
public interface Call extends Cloneable {
/** 获得原始请求 */
Request request();
/** 同步执行请求 */
Response execute() throws IOException;
/** 异步执行请求 */
void enqueue(Callback responseCallback);
/** 尽可能取消请求。已经完成了的请求不能被取消 */
void cancel();
/**
* 调用了execute()或者enqueue(Callback)后都是true
*/
boolean isExecuted();
boolean isCanceled();
/** 创建一个新的、完全一样的Call对象,即使原对象状态为enqueued或者executed */
Call clone();
interface Factory {
Call newCall(Request request);
}
}
回到RealCall ,看看他的构造方法和成员变量:
final OkHttpClient client;
final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;
final EventListener eventListener;
/** The application's original request unadulterated by redirects or auth headers. */
final Request originalRequest;
final boolean forWebSocket;
// Guarded by this.
private boolean executed;
RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
// 事件监听器,默认为null,目前3.8版本也无法设置
final EventListener.Factory eventListenerFactory = client.eventListenerFactory();
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
// 创建一个重试、重定向拦截器
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
// TODO(jwilson): this is unsafe publication and not threadsafe.
this.eventListener = eventListenerFactory.create(this);
}
RealCall的同步请求方法
RealCall.excute()
@Override
public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
//捕获调用栈跟踪
captureCallStackTrace();
eventListener.callStart(this);
try {
//发起请求
client.dispatcher().executed(this);
//获取请求结果
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
}
}
上面的方法一旦执行就就将executed设置为true,表示该请求已经执行了,你能在执行。
然后就继续调用 client.dispatcher().executed(this); 该方法就是将call添加到Dispather.runningSyncCalls中去,
synchronized void executed(RealCall call) {
//一个Deque容器想,和ArrayList差不多(内部也是用数组实现, 区别在于扩容上)
runningSyncCalls.add(call);
}
接着调用getResponseWithInterceptorChain()方法进行网络请求,返回Reponse结果。具体后面讲异步在进行详细介绍。
最后调用了client.dispatcher().finished(this); 该方法就是call执行完毕和将call从Dispather.runningSyncCalls中移除出去。见代码
void finished(RealCall call) {
finished(runningSyncCalls, call, false);
}
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
//使用对象锁保证线程安全
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
同时,如果promoteCalls为true(此处入参false),还会执行promoteCalls方法,此方法是给异步调用准备的,具体代码后面会谈到;
最后如果runningAsyncCalls、runningSyncCalls这俩正在执行的同步、异步队列之和为0,说明dispatcher处理空闲状态,那么调用idleCallback.run通知外界dispatcher已经空闲了。到此此处同步方法已经初步结束。
RealCall的异步方法enqueue
同execute方法类似,一旦Call.enqueue方法被执行,那么其executed就会被设置为true,如果多次调用就会报错。
然后调用client.dispatcher().enqueue(new AsyncCall(responseCallback));方法开始了异步调用。
源码:
@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));
}
看下AsyncCall的相关代码
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
...
@Override protected void execute() {
...
}
}
他是继承一个NamedRunnable类,是具有一个name属性的Runnable的抽象类,在执行方法的时候为执行该代码的当前线程设置名字
/**
* Runnable implementation which always sets its thread name.
*/
public abstract class NamedRunnable implements Runnable {
protected final String name;
public NamedRunnable(String format, Object... args) {
this.name = Util.format(format, args);
}
@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();
}
下面接续返回Dispatcher.enqueue方法:
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
maxRequests和maxRequestsPerHost都有默认值,且有setter方法可以设置具体值,两者的setter方法最后会执行promoteCalls方法尝试执行异步任务。
在enqueue方法中,首先会检查「正在运行的异步请求数」以及「call对应的host上的异步请求数」是否达到了阈值。如果还没有达到阈值,那么加入到runningAsyncCalls队列中,同时开始执行请求;否则加入到readyAsyncCalls队列中进行等待.
executorService().execute(call),这是一个单例实现的线程池,该线程值也可以在Dispatcher的构造器中传入。
/** Executes calls. Created lazily. */
private @Nullable ExecutorService executorService;
public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}
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;
}
提交到线程池后,AsyncCall.run方法就会被调用,又因为AsyncCall继承了NamedRunnable,所以最后执行的是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);
}
}
首先调用getResponseWithInterceptorChain进行网络请求并获取Response,然后根据请求是否被取消,调用对应的回调方法。最后调用client.dispatcher().finished(this)方法在runningAsyncCalls方法中移除call,并尝试执行其他的异步方法。
我们先看client.dispatcher().finished(this)方法,最后再看getResponseWithInterceptorChain方法的实现:
/** Used by {@code AsyncCall#run} to signal completion. */
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
private void promoteCalls() {
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();
if (runningCallsForHost(call) < maxRequestsPerHost) {
i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);
}
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
2个finshed方法不用说了,将同步的时候已经介绍了。
promoteCalls方法的代码也很清晰明了,这是一个将等待队列中的任务移到runningAsyncCalls且执行的过程。
getResponseWithInterceptorChain
此处是拦截器,重头戏来了话不多说看下源码:
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);
}
首先将以下拦截器依次加入到List中:
OkHttpClient设置的拦截器interceptors()
重试、重定向拦截器RetryAndFollowUpInterceptor
把用户请求转换为服务器请求、把服务器返响应转换为用户响应的BridgeInterceptor
读取缓存直接返回、将响应写入到缓存中的CacheInterceptor
与服务器建立连接的ConnectInterceptor
OkHttpClient设置的网络拦截器networkInterceptors()
真正执行网络请求的CallServerInterceptor
将所有的拦截器保存在interceptors后,创建一个拦截器责任链RealInterceptorChain,并调用其proceed开始处理网络请求。
Okhttp3的五大拦截器的作用的简单介绍
RetryAndFollowUpInterceptor(重定向拦截器)
源码:
private static final int MAX_FOLLOW_UPS = 20;
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Call call = realChain.call();
EventListener eventListener = realChain.eventListener();
/**
* todo 管理类,维护了 与服务器的连接、数据流与请求三者的关系。真正使用的拦截器为 Connect
*/
StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(request.url()), call, eventListener, callStackTrace);
this.streamAllocation = streamAllocation;
int followUpCount = 0;
Response priorResponse = null;
while (true) {
if (canceled) {
streamAllocation.release();
throw new IOException("Canceled");
}
Response response;
boolean releaseConnection = true;
try {
//todo 请求出现了异常,那么releaseConnection依旧为true。
response = realChain.proceed(request, streamAllocation, null, null);
releaseConnection = false;
} catch (RouteException e) {
//todo 路由异常,连接未成功,请求还没发出去
//The attempt to connect via a route failed. The request will not have been sent.
if (!recover(e.getLastConnectException(), streamAllocation, false, request)) {
throw e.getLastConnectException();
}
releaseConnection = false;
continue;
} catch (IOException e) {
//todo 请求发出去了,。(socket流正但是和服务器通信失败了在读写数据的时候断开连接)
// HTTP2才会抛出ConnectionShutdownException。所以对于HTTP1 requestSendStarted一定是true
//An attempt to communicate with a server failed. The request may have been sent.
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
if (!recover(e, streamAllocation, requestSendStarted, request)) throw e;
releaseConnection = false;
continue;
} finally {
// We're throwing an unchecked exception. Release any resources.
//todo 不是前两种的失败,那直接关闭清理所有资源
if (releaseConnection) {
streamAllocation.streamFailed(null);
streamAllocation.release();
}
}
//todo 如果进过重试/重定向才成功的,则在本次响应中记录上次响应的情况
//Attach the prior response if it exists. Such responses never have a body.
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(
priorResponse.newBuilder()
.body(null)
.build()
)
.build();
}
//todo 处理3和4xx的一些状态码,如301 302重定向
Request followUp = followUpRequest(response, streamAllocation.route());
if (followUp == null) {
if (!forWebSocket) {
streamAllocation.release();
}
return response;
}
closeQuietly(response.body());
//todo 限制最大 followup 次数为20次
if (++followUpCount > MAX_FOLLOW_UPS) {
streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
if (followUp.body() instanceof UnrepeatableRequestBody) {
streamAllocation.release();
throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
}
//todo 判断是不是可以复用同一份连接
if (!sameConnection(response, followUp.url())) {
streamAllocation.release();
streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(followUp.url()), call, eventListener, callStackTrace);
this.streamAllocation = streamAllocation;
} else if (streamAllocation.codec() != null) {
throw new IllegalStateException("Closing the body of " + response
+ " didn't close its backing stream. Bad interceptor?");
}
request = followUp;
priorResponse = response;
}
}
客户端向服务器发送一个请求,获取对应的资源,服务器收到请求之后,发现这个资源在另一个位置,浴室读物器在返回的响应头的location字段中写入那个请求资源的正确地址,并设置reponse的状态码为30X。
重定向拦截器的拦截操作中做了这么几件事:
1、创建一个 StreamAllocation。
2、调用下一个chain 。
3、判断是否重定向。重定向会对request做一些修改,返回新的request,否则会返回null。
4、followUp == null 无重定向,释放资源,直接返回response。
5、比较重定向前后的 host、port、scheme是否一致,一致的话复用,否则重新创建 StreamAllocation。
6、通过 while (true) ,重复步骤 2
BridgeInterceptor(桥接拦截器)
BridgeInterceptor 负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应。
源码:
/**
* 为我们补全请求头,并默认使用gzip压缩,同时将响应体重新设置为gzip读取。
* @param chain
* @return
* @throws IOException
*/
@Override
public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
RequestBody body = userRequest.body();
if (body != null) {
MediaType contentType = body.contentType();
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString());
}
long contentLength = body.contentLength();
if (contentLength != -1) {
requestBuilder.header("Content-Length", Long.toString(contentLength));
requestBuilder.removeHeader("Transfer-Encoding");
} else {
requestBuilder.header("Transfer-Encoding", "chunked");
requestBuilder.removeHeader("Content-Length");
}
}
if (userRequest.header("Host") == null) {
requestBuilder.header("Host", hostHeader(userRequest.url(), false));
}
if (userRequest.header("Connection") == null) {
requestBuilder.header("Connection", "Keep-Alive");
}
// If we add an "Accept-Encoding: gzip" header field we're responsible for also
// decompressing
// the transfer stream.
boolean transparentGzip = false;
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
transparentGzip = true;
requestBuilder.header("Accept-Encoding", "gzip");
}
List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
if (!cookies.isEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies));
}
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", Version.userAgent());
}
Response networkResponse = chain.proceed(requestBuilder.build());
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);
if (transparentGzip
&& "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
&& HttpHeaders.hasBody(networkResponse)) {
GzipSource responseBody = new GzipSource(networkResponse.body().source());
Headers strippedHeaders = networkResponse.headers().newBuilder()
.removeAll("Content-Encoding")
.removeAll("Content-Length")
.build();
responseBuilder.headers(strippedHeaders);
String contentType = networkResponse.header("Content-Type");
responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
}
return responseBuilder.build();
}
1、如果这个请求有请求体,就添加 Content-Type, Content-Length等。
2、如果这个请求没有 Host,就通过 url 来获取 Host 值添加到 Header中。
3、如果这个请求没有接收的数据类型Accept-Encoding,且没指定接收的数据范围,就添加默认接受格式为 gzip。
5、去 CookieJar 中根据 url 查询 Cookie 添加到 Header。
6、如果当前没有,就添加 User-Agent 信息。
发起请求后:
7、解析响应 Header 中的 Cookie
8、如果想要数据的格式是 gzip,就创建 GzipSource 进行解压,同时移除 Content-Encoding 和 Content-Length
CacheInterceptor(缓存拦截器)、
第三个拦截器是缓存处理拦截器 CacheInterceptor,它的重要性用一句话来描述:最快的请求就是不请求,直接用缓存。
源码:
@Override
public Response intercept(Chain chain) throws IOException {
//todo 通过url的md5数据 从文件缓存查找 (GET请求才有缓存)
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;
long now = System.currentTimeMillis();
//todo 缓存策略:根据各种条件(请求头)组成 请求与缓存
CacheStrategy strategy =
new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
//
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;
if (cache != null) {
cache.trackResponse(strategy);
}
if (cacheCandidate != null && cacheResponse == null) {
closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
}
//todo 没有网络请求也没有缓存
//If we're forbidden from using the network and the cache is insufficient, fail.
if (networkRequest == null && cacheResponse == null) {
return new Response.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(504)
.message("Unsatisfiable Request (only-if-cached)")
.body(Util.EMPTY_RESPONSE)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
}
//todo 没有请求,肯定就要使用缓存
//If we don't need the network, we're done.
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
//todo 去发起请求
Response networkResponse = null;
try {
networkResponse = chain.proceed(networkRequest);
} finally {
// If we're crashing on I/O or otherwise, don't leak the cache body.
if (networkResponse == null && cacheCandidate != null) {
closeQuietly(cacheCandidate.body());
}
}
// If we have a cache response too, then we're doing a conditional get.
if (cacheResponse != null) {
//todo 服务器返回304无修改,那就使用缓存的响应修改了时间等数据后作为本次请求的响应
if (networkResponse.code() == HTTP_NOT_MODIFIED) {
Response response = cacheResponse.newBuilder()
.headers(combine(cacheResponse.headers(), networkResponse.headers()))
.sentRequestAtMillis(networkResponse.sentRequestAtMillis())
.receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
networkResponse.body().close();
// Update the cache after combining headers but before stripping the
// Content-Encoding header (as performed by initContentStream()).
cache.trackConditionalCacheHit();
cache.update(cacheResponse, response);
return response;
} else {
closeQuietly(cacheResponse.body());
}
}
//todo 走到这里说明缓存不可用 那就使用网络的响应
Response response = networkResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
//todo 进行缓存
if (cache != null) {
if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response,
networkRequest)) {
// Offer this request to the cache.
CacheRequest cacheRequest = cache.put(response);
return cacheWritingResponse(cacheRequest, response);
}
if (HttpMethod.invalidatesCache(networkRequest.method())) {
try {
cache.remove(networkRequest);
} catch (IOException ignored) {
// The cache cannot be written.
}
}
}
return response;
}
1、根据Request和之前缓存的Response得到CacheStrategy
2、根据CacheStrategy决定是请求网络还是直接返回缓存
3、如果 step 2中决定请求网络,则在这一步将返回的网络响应和本地缓存对比,对本地缓存进行改增删操作
ConnectInterceptor(链接拦截器)
作用:TCP 协议需要需要建立连接才能进行通信,每次请求都建立连接会极大地影响通信效率。
OkHttp 的优点之一优化了连接的建立:内部维护了可以重复使用的 Socket 连接池,减少握手次数,加快请求响应。
源码:
@Override
public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
StreamAllocation streamAllocation = realChain.streamAllocation();
// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean doExtensiveHealthChecks = !request.method().equals("GET");
HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
RealConnection connection = streamAllocation.connection();
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
1、连接 RealConnection 是对 Socket 的封装。
2、OkHttp 的连接复用主要是通过 StreamAllocation 来实现的,每个连接上持有一个。
3、StreamAllocation 引用的列表,以此来标识当前连接是否空闲。它里面维护了List的引用。List中StreamAllocation的数量也就是socket被引用的计数,如果计数为0的话,说明此连接没有被使用就是空闲的,需要通过下文的算法实现回收;如果计数不为0,则表示上层代码仍然引用,就不需要关闭连接。
4、判断连接是否可以重用,除了比较连接当前的 host,也可以比较路由信息。
5、连接池在添加新连接时会运行清理任务,默认最多空闲连接为 5,最长空闲时间为 5 分钟。
CallServeInteceptor(读写拦截器)
作用:它负责实现网络 IO,真正的与服务器进行链接,所有拦截器都要依赖它才能拿到响应数据。
源码:
@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;
//如果有请求体,将body发送给服务器
if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
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()) {
//如果未满足“Expect: 100-continue”期望,请防止重用 HTTP1 连接。否则,我们仍然有义务传输请求主体以使连接保持一致状态
streamAllocation.noNewStreams();
}
}
//完成请求
httpCodec.finishRequest();
if (responseBuilder == null) {
realChain.eventListener().responseHeadersStart(realChain.call());
//解析http的响应的header
responseBuilder = httpCodec.readResponseHeaders(false);
}
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
int code = response.code();
if (code == 100) {
//即使没有请求,服务器也继续发送100-continue。客户端继续读取
responseBuilder = httpCodec.readResponseHeaders(false);
response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
code = response.code();
}
realChain.eventListener()
.responseHeadersEnd(realChain.call(), response);
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();
}
//如果请求合着服务器响应的response中的header的connection的值为close,就关闭链接
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;
}
1、CallServerInterceptor 首先会将请求中的 header 写给服务器。
2、如果有请求体的话,会再将 body 发送给服务器。
3、最后通过httpCodec.finishRequest() 结束 http 请求的发送。
4、然后从连接中读取服务器的响应,并构造 Response。
5、如果请求的 header或服务器响应的 header 中,Connection 的值为 close,就关闭连接。
6、最后返回 Response。
拦截器总结
okhttp使用的是责任链的设计模式,从上到下依次执行。 拦截器的讲解并没有具体的深入探究源码,只介绍了大概的流程和做的事情。有兴趣的同学自己可以深入源码看下