Okhttp的源码解读
Posted 风雨田
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Okhttp的源码解读相关的知识,希望对你有一定的参考价值。
重要的类
类名 | 描述 |
---|---|
OkHttpClient | OkHttp请求客户端,Builder模式实现 |
Dispatcher | 本质是异步请求的调度器,负责调度异步请求的执行,控制最大请求并发数和单个主机的最大并发数,并持有有一个线程池负责执行异步请求,对同步请求只是作统计操作。 |
Request | 封装网络请求,就是构建请求参数(如url,header,请求方式,请求参数),Builder模式实现 |
Response | 网络请求对应的响应,Builder模式实现,真正的Response是通过RealCall.getResponseWithInterceptorChain()方法获取的。 |
Call | 是根据Request生成的一个具体的请求实例,且一个Call只能被执行一次。 |
ConnectionPool | 连接池 |
Interceptor | Interceptor可以说是OkHttp的核心功能,它就是通过Interceptor来完成监控管理,重写和重试请求的。 |
Cache | 可以自定义是否采用缓存,缓存形式是磁盘缓存,DiskLruCache。 |
源码思想
OkHttp3,网络请求库,同步请求RealCall.execute()和异步请求RealCall.enqueue(),请求任务都是交给Dispatcher调度请求任务的处理,请求通过一条拦截链,每一个拦截器处理一部分工作,最后一个拦截器,完成获取请求任务的响应,会将响应沿着拦截链向上传递。
源码流程
初始化一个OkHttpClient对象
OkHttpClient mOkHttpClient = new OkHttpClient();
public OkHttpClient()
this(new Builder());
Builder是OkHttpClient的一个内部类,目的是构造和封装数据
public Builder()
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
proxySelector = ProxySelector.getDefault();
cookieJar = CookieJar.NO_COOKIES;
socketFactory = SocketFactory.getDefault();
hostnameVerifier = OkHostnameVerifier.INSTANCE;
certificatePinner = CertificatePinner.DEFAULT;
proxyAuthenticator = Authenticator.NONE;
authenticator = Authenticator.NONE;
connectionPool = new ConnectionPool();
dns = Dns.SYSTEM;
followSslRedirects = true;
followRedirects = true;
retryOnConnectionFailure = true;
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
创建一个请求
Request类是HTTP请求,它携带了请求地址、请求方法、请求头部、请求体以及其他信息。它也是通过Builder模式创建的。
Request request = new Request.Builder()
.url(url)
.build();
public Request build()
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
private Request(Builder builder)
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build();
this.body = builder.body;
this.tag = builder.tag != null ? builder.tag : this;
接下来对应的就是请求,请求分为异步与同步,先看同步
Response response = mOkHttpClient.newCall(request).execute();
Response是HTTP响应,它继承自Closeable(Closeable继承自AutoCloseable,AutoCloseable资源自动关闭的接口),它携带了请求、网络请求协议、返回状态码、请求头、响应体等等,其中,网络请求协议是Protocol,Protocol是一个枚举类型,包含4中类型
public enum Protocol
HTTP_1_0("http/1.0"),
HTTP_1_1("http/1.1"),
SPDY_3("spdy/3.1"),
HTTP_2("h2");
Call类是一个抽象类
OkHttpClient.newCall(request);
@Override
public Call newCall(Request request)
return new RealCall(this, request); //RealCall extends Call
protected RealCall(OkHttpClient client, Request originalRequest)
this.client = client;
this.originalRequest = originalRequest;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client);
public interface Call
Request request();
Response execute() throws IOException;
void enqueue(Callback responseCallback);
void cancel();
boolean isExecuted();
boolean isCanceled();
interface Factory
Call newCall(Request request);
OKHttp提供了execute()方法(同步方法)和enqueue()方法(异步方法),下面我们先看看execute()方法(同步方法)
execute()方法,首先判断是否已执行过,如果已经执行过,则抛出异常信息,也就是说一次Call实例只能调用一次execute()方法。没有执行则通过分发器进行执行
@Override
public Response execute() throws IOException
synchronized (this)
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
try
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
finally
client.dispatcher().finished(this);
分发器将请求加入到队列
...
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();//双端队列
...
/** Used by @code Call#execute to signal it is in-flight. */
synchronized void executed(RealCall call)
runningSyncCalls.add(call);
最后通过分发器的finished()方法结束请求
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();
所以通过上面信息可知真正发送请求的地方是getResponseWithInterceptorChain()方法
真正发送请求源码分析
组装各种拦截器,然后发送请求
private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException
Interceptor.Chain chain = new ApplicationInterceptorChain(0, originalRequest, forWebSocket);
return chain.proceed(originalRequest);
拦截器接口
public interface Interceptor
Response intercept(Chain chain) throws IOException;
interface Chain
Request request();
Response proceed(Request request) throws IOException;
Connection connection();
在这个责任链模式中执行每一个连接器的proceed方法,然后我们逐个分析这些连接器
这里是责任链模式,由用户主动调用此方法,因此需要实现拦截的功能
这里如果有Intercepter实现已经添加,则为每一个拦截器创建一个ApplicationInterceptorChain然后递归直到将所有的链调用完成最后进入getResponse方法中。
@Override public Response proceed(Request request) throws IOException
//If there's another interceptor in the chain, call that.
if (index < client.interceptors().size())
//新建一个环节,并且将索引+1,这里的request可能不再是originalRequest了,因为在拦截器中可能被修改了
Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);
//获取到对应的拦截器
Interceptor interceptor = client.interceptors().get(index);
//执行拦截器的intercept方法,参数是上面新建的环节,这个方法里面会调用chain.proceed(),递归了。
//在最深的一层调用getResponse之后,响应会一层层的往外传
Response interceptedResponse = interceptor.intercept(chain);
if (interceptedResponse == null)
throw new NullPointerException("application interceptor " + interceptor
+ " returned null");
// 返回这一层拦截器处理用的响应
return interceptedResponse;
//递归到了最深的一层,拦截器都进入过了,发送httpRequest,获取到response.
return getResponse(request, forWebSocket);
当执行完getResponse完成之后就一步步返回。
真正请求数据的方法
- request.newBuilder();由于是之前的请求,所以内部调用newBuilder的时候使用老请求的url,method,body,tag,headers。然后给这个requestBuilder中添加各种请求的键值对信息最后构造一个原信息+新信息的新请求。
- 创建一个请求引擎,每一个请求引擎代表依次请求/响应
- 在死循环中做的事情
- 如果请求被取消抛出异常
- 然后发送请求,读取响应,重新连接设置成false
- 如果请求失败则算了不请求了,但是如果是路由异常则需要恢复请求。
- 最后释放链接,得到响应。
- 如果存在下一步请求,则重定向。
Response getResponse(Request request, boolean forWebSocket) throws IOException
// 复制请求头,并设置适合的属性。如果长度不为-1,则设置Content-Length,否则使用chunked方式传输。
// 如果对chunked不熟悉,请参考其他资料
RequestBody body = request.body();
if (body != null)
// 根据传进来的request创建新的Builder.
Request.Builder requestBuilder = request.newBuilder();
// 设置Content-Type
MediaType contentType = body.contentType();
if (contentType != null)
requestBuilder.header("Content-Type", contentType.toString());
// 判断使用何种方式传输
long contentLength = body.contentLength();
// body长度不为-1,设置Content-Length
if (contentLength != -1)
requestBuilder.header("Content-Length", Long.toString(contentLength));
requestBuilder.removeHeader("Transfer-Encoding");
else
// body 长度为 -1 ,使用chunked传输
requestBuilder.header("Transfer-Encoding", "chunked");
requestBuilder.removeHeader("Content-Length");
//创建新请求
request = requestBuilder.build();
// 创建一个新的引擎,每个引擎代表一次请求/响应对
engine = new HttpEngine(client, request, false, false, forWebSocket, null, null, null);
int followUpCount = 0; //重试次数
//死循环 出口:
// 1. 请求被取消
// 2. 请求有问题
// 3. 捕获到异常,且尝试恢复失败
// 4. 获取到响应,且无需重定向
// 5. 重定向次数超过最大限制
while (true)
// 被取消的情况
if (canceled)
engine.releaseStreamAllocation();
throw new IOException("Canceled");
boolean releaseConnection = true;
try
// 发送请求
engine.sendRequest();
// 读取响应
engine.readResponse();
releaseConnection = false;
catch (RequestException e)
// 请求失败,请求本身有问题,或者是网络不通
throw e.getCause();
catch (RouteException e)
// 连接到服务器的路由发生异常,请求还没被发送
// 通过上一次连接异常恢复引擎
HttpEngine retryEngine = engine.recover(e.getLastConnectException(), null);
// 如果恢复成功,将当前的引擎设置为这个恢复好的引擎
if (retryEngine != null)
releaseConnection = false;
engine = retryEngine;
continue;
// 没法恢复,抛出异常
throw e.getLastConnectException();
catch (IOException e)
// 与服务器交互失败,这时,请求可能已经被发送
// 恢复引擎
HttpEngine retryEngine = engine.recover(e, null);
//如果恢复成功,将当前的引擎设置为这个恢复好的引擎
if (retryEngine != null)
releaseConnection = false;
engine = retryEngine;
continue;
// 没法恢复,抛出异常
throw e;
finally
// 如果需要释放连接,则将连接释放
if (releaseConnection)
StreamAllocation streamAllocation = engine.close();
streamAllocation.release();
// 获取响应
Response response = engine.getResponse();
// 下一步的请求,如果存在,则需要重定向
Request followUp = engine.followUpRequest();
//如果不需要重定向
if (followUp == null)
if (!forWebSocket)
// 释放连接
engine.releaseStreamAllocation();
//返回响应
return response;
// 如果需要重定向,关闭当前引擎
StreamAllocation streamAllocation = engine.close();
// 如果超过最大数,释放连接,并抛出异常
if (++followUpCount > MAX_FOLLOW_UPS)
// 释放连接
streamAllocation.release();
// 抛出异常
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
// 如果重定向的地址和当前的地址一样,则不需要释放连接
if (!engine.sameConnection(followUp.url()))
streamAllocation.release();
streamAllocation = null;
// 使用重定向后的请求,重新实例一个引擎,开始下一次循环
request = followUp;
engine = new HttpEngine(client, request, false, false, forWebSocket, streamAllocation, null,
response);
最后HttpEngine进行的操作与http协议很大相关,作者对此不感兴趣,本文只是对流程做大概分析
哥哥若是喜欢可移驾公众号:码老板
以上是关于Okhttp的源码解读的主要内容,如果未能解决你的问题,请参考以下文章