一、介绍
OKHttp是一款高效的HTTP客户端,支持连接同一地址的链接共享同一个socket,通过连接池来减小响应延迟,还有透明的GZIP压缩,请求缓存等优势,其核心主要有路由、连接协议、拦截器、代理、安全性认证、连接池以及网络适配,拦截器主要是指添加,移除或者转换请求或者回应的头部信息(新版4.x.x是使用kotlin写的,所以我们这里分析的是3.14.x的版本)
二、使用
OkHttpClient okHttpClient=new OkHttpClient();
final Request request=new Request.Builder()
.url("https://www.wanandroid.com/navi/json")
.get()
.build();
final Call call = okHttpClient.newCall(request);
try {
Response response = call.execute();
Log.e("同步结果---- ",response.body().string()+"");
} catch (IOException e) {
e.printStackTrace();
}
- 构造OKHttpClient
- 构造Request
- 得到一个Call
- 同步或异步执行Call,对应execute()和enqueue方法(线程池最终也是调用execute())
这里有个任务分发器Dispatcher
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
private @Nullable Runnable idleCallback;
//线程池对象
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<>();
这个来用来操作控制任务请求,使用Deque双端队列操作
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
timeout.enter();
eventListener.callStart(this);
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
e = timeoutExit(e);
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
}
}
方法的重点就是返回的Response result = 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);
}
这里是okhttp的精髓Interceptor,使用了责任链模式,类似装饰着模式,层层封装,给request和Response做不同的处理,View的点击事件使用的就是责任链模式,可以拦截和消费事件。当然我们可以添加自己的Interceptor。
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;
}
Interceptor的顺序分别是自定义Interceptor,RetryAndFollowUpInterceptor请求重定向拦截器,重定向拦截器初始化了StreamAllocation对象,这个对象包含请求的一系列参数,每个请求都会有一个StreamAllocation对象,但他们共用Call对象和Address对象,Address包含url,dns,socket和proxy等处理对象,也就是说,RetryAndFollowUpInterceptor中初始化了一系列请求初始化的参数,然后调用下一个拦截器
BridgeInterceptor
在BridgeInterceptor中拼接了请求头信息,比如支持gzip格式的参数
对返回的response做处理
//响应header, 如果没有自定义配置cookieJar==null,则什么都不做
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);
//判断服务器是否支持gzip压缩格式,如果支持则交给kio压缩
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)));
CacheInterceptor
http缓存分为两种,一种强制缓存,一种对比缓存,强制缓存生效时直接使用以前的请求结果,无需发起网络请求。对比缓存生效时,无论怎样都会发起网络请求,如果请求结果未改变,服务端会返回304,但不会返回数据,数据从缓存中取,如果改变了会返回数据。
//如果缓存和返回都是空的,直接返回504错误
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();
}
// 如果强制读缓存,就去缓存读取
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
Response networkResponse = null;
try {
//调用下一个Interceptor
networkResponse = chain.proceed(networkRequest);
} finally {
// 报错回收资源
if (networkResponse == null && cacheCandidate != null) {
closeQuietly(cacheCandidate.body());
}
}
if (cacheResponse != null) {
// 如果返回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());
}
}