OkHttp是square公司出品的一款网络加载框架,咱们今天从源码的角度来看看,他在咱们进行同步和异步请求的时候,内部都具体作了什么操做。web
在使用OkHttp的时候,首先第一步是实例化一个OkHttpClient对象。设计模式
OkHttpClient okHttpClient = new OkHttpClient();
复制代码
咱们来看看他的构造方法。bash
public OkHttpClient() {
this(new Builder());
}
OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
this.protocols = builder.protocols;
this.connectionSpecs = builder.connectionSpecs;
this.interceptors = Util.immutableList(builder.interceptors);
this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
this.eventListenerFactory = builder.eventListenerFactory;
this.proxySelector = builder.proxySelector;
this.cookieJar = builder.cookieJar;
this.cache = builder.cache;
this.internalCache = builder.internalCache;
this.socketFactory = builder.socketFactory;
boolean isTLS = false;
for (ConnectionSpec spec : connectionSpecs) {
isTLS = isTLS || spec.isTls();
}
if (builder.sslSocketFactory != null || !isTLS) {
this.sslSocketFactory = builder.sslSocketFactory;
this.certificateChainCleaner = builder.certificateChainCleaner;
} else {
X509TrustManager trustManager = Util.platformTrustManager();
this.sslSocketFactory = newSslSocketFactory(trustManager);
this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
}
if (sslSocketFactory != null) {
Platform.get().configureSslSocketFactory(sslSocketFactory);
}
this.hostnameVerifier = builder.hostnameVerifier;
this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
certificateChainCleaner);
this.proxyAuthenticator = builder.proxyAuthenticator;
this.authenticator = builder.authenticator;
this.connectionPool = builder.connectionPool;
this.dns = builder.dns;
this.followSslRedirects = builder.followSslRedirects;
this.followRedirects = builder.followRedirects;
this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
this.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
this.writeTimeout = builder.writeTimeout;
this.pingInterval = builder.pingInterval;
if (interceptors.contains(null)) {
throw new IllegalStateException("Null interceptor: " + interceptors);
}
if (networkInterceptors.contains(null)) {
throw new IllegalStateException("Null network interceptor: " + networkInterceptors);
}
}
复制代码
能够看到,其构造方法里面的参数很是很是多,包括于对socket证书校验、设置读取和写入的超时时间、设置拦截器、链接池的操做等等。能够说若是咱们想使用OkHttp内部默认的设置,那么咱们能够直接new一个OkHttpClient就能够,可是若是咱们想按照本身的方法来设置一些参数,那么咱们可使用Builder来处理。cookie
final String URL = "";
OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder()
.url(URL)
.get()
.build();
try {
Response response = okHttpClient.newCall(request).execute();
String result = response.body().string();
} catch (IOException e) {
e.printStackTrace();
}
复制代码
咱们能够看到,通常的网络请求分为三步。第一为实例化一个OKHttpClient,第二步实例化一个Request,第三步经过execute()发起同步的网络请求。咱们逐步来分析。因为刚刚已经分析过了okHttpClient,因此这里再也不分析,咱们从request开始。网络
Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build();
this.body = builder.body;
this.tags = Util.immutableMap(builder.tags);
}
复制代码
点击进入Request里面咱们能够看到里面的构造方法,其实里面的功能并很少,构造方法里面的参数决定了其功能。看到,Reuqest的主要功能就几个,第一为url,也就是请求的地址。第二为method,请求的方式(GET请求或者POST请求)。headers请求头,body为请求体。最后有一个tags,这个是干吗的呐?其实这个tags是在给一个网络请求作标记,若是咱们在进行网络请求的时候出了什么问题,须要取消掉该网络请求,那么就在发起网络请求的时候用tags先标记下,而后根据标记来取消网络请求。框架
咱们点进newCall里面看看。异步
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
复制代码
能够看到在newCall内部其实用的是newRealCall方法,等于说全部的网络请求操做都是在newRealCall里面进行的操做。那么就很好办了,咱们直接经过newRealCall来看看,内部进行的同步操做和异步操做的过程是怎么样的。socket
@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);
}
}
复制代码
咱们从源码里面能够看到,一个同步的网络请求总共作了四件事。ide
1.if判断,检查这个executed是否正在执行,若是执行就直接抛出异常。这里须要说明一点,一个网络请求的操做,不能同时被操做两次,若是操做两次,则报错。ui
2.经过分发器(又能够叫调度器)dispatcher来执行同步任务操做。关于这个dispatcher这里要说明一下,不少状况下,其实这个调度器是在异步操做里面才会用到,其实在同步里面也作了操做。关于dispatcher有机会我专门写一篇文章来说解。
3.经过getResonseWithInterceptorChain()将结果返回。
4.经过dispatcher结束掉任务finished()。
其实经过上面几步,咱们仍是没有看出来网络请求到底如何发起的,那么究竟是在哪里作了操做网络请求的呐?答案就在第三步getResonseWithInterceptorChain()。咱们看下这个源码。
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的核心了。在这里,首先会new一个拦截器的集合,以后把咱们本身定义的拦截器和okhttp自带的拦截器一块儿放入到这个集合当中去,最后放入到一个叫作拦截器链RealInterceptorChain里面,经过proceed发起请求,从而实现网络请求操做。说道拦截器,这里须要带一笔,拦截器能够说是okhttp的精华核心部分,在我看来,okhttp之因此牛逼就是牛逼在这些拦截器上面。咱们能够经过不一样的需求添加和改造不一样的拦截器,而且这些拦截器之间的耦合度很是的低,等于说在改造一种拦截器以后,并不会对另外的拦截器有任何的影响。能够说下降耦合度是写代码的最高境界,这相比较volley不知道强了多少倍。
咱们能够看到,在拦截器这里使用了责任链的设计模式。他经过一个一个的拦截器,根据添加顺序来环环相扣,执行完一个拦截以后再执行另一个,最后把他们凑成一个环chain,放入到RealInterceptorChain当中去。咱们再来看下RealInterceptorChain方法里面操做。
public final class RealInterceptorChain implements Interceptor.Chain {
private final List<Interceptor> interceptors;
private final StreamAllocation streamAllocation;
private final HttpCodec httpCodec;
private final RealConnection connection;
private final int index;
private final Request request;
private int calls;
public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
HttpCodec httpCodec, RealConnection connection, int index, Request request) {
this.interceptors = interceptors;
this.connection = connection;
this.streamAllocation = streamAllocation;
this.httpCodec = httpCodec;
this.index = index;
this.request = request;
}
@Override public Connection connection() {
return connection;
}
public StreamAllocation streamAllocation() {
return streamAllocation;
}
public HttpCodec httpStream() {
return httpCodec;
}
@Override public Request request() {
return request;
}
@Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpCodec, connection);
}
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);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
......
return response;
}
}
复制代码
从源码能够看到,其实在RealInterceptorChain方法中除了赋值之外其余并无作什么特别的操做。那么咱们能够知道,主要的操做实际上是在这个类的proceed方法里面作了操做。在proceed的方法里面咱们能够看到,作了两件事。第一,建立下一个拦截器,而且把i(ndex + 1)传入里面。第二,经过index获取到拦截器,而且将下一个拦截器链放入到这个拦截器里面。由此能够看到,拦截器的执行是层层递进,每次都是执行下一个拦截器,由下一个拦截器来完成方法操做。由网上找来的一张图咱们能够看到这里面的具体操做。
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
复制代码
在说明异步操做的时候,咱们须要先了解一下任务队列的概念。在okhttp中有三种任务队列和一种线程池。
readyAsyncCalls: 待执行异步任务队列
runningAsyncCalls: 运行中异步任务队列
runningSyncCalls: 运行中同步任务队列
executorService: 任务队列线程池
当能够执行异步任务的列队数大于线程中能够请求的最大请求数量时,则证实线程池比较繁忙,不能再往里面添加任务,因此先把他放到等待异步执行的任务队列当中去,等到线程池有空间执行再把他取出来。若是线程池有空间,则直接将call任务放到运行中异步任务队列中去,而后经过exectorService线程池来启动执行。异步操做其实本质上和同步操做相似,只是多了这么个等待和运行的概念而已。
下面经过一张图来展现okhttp总体的发起请求的流程,到此okhttp发起网络请求部分完成。