原文链接如下
http://blog.csdn.net/qq_34902522/article/details/79483678
前言
对OKhttp的源码看了有一段时间了,感觉有点体会,准备写点东西,深刻深刻,不然过段时间就忘了O(∩_∩)O哈哈~
网上已经有了很多对OKhttp3的源码分析文章,说的也都比较好,但是觉得还是看别人写的再好终究是别人的东西,自己写的过程中会对OKhttp的理解更加深刻,而且1000个人的眼里有一千个哈姆雷特,每个人对OKhttp的理解,认识,还是会有不同的。
对OKhttp的源码分析,一篇文章也是说不完的,能说完的话,那也太省略,长篇大论的文章,也很难让人看下去,所以,准备写一个关于OKhttp源码的分析的系列文章,本篇主要写的是OKhttp的工作流程,并对流程中的要点进行简要概述,后续的文章中在精要分析。
概述
官网OverView
看官方的最准确。
基本用法
先看基本用法,通过他的用法,来分析他的原理。
OKhttp请求分为同步请求和异步请求,代码如下:
同步请求:
OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.build();
Response response = client.newCall(request).execute();
return response.body().string();
}
复制代码
异步请求:
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(url)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
复制代码
同步请求比较简单,所以我们来分析,异步请求,异步请求的原理明白了,同步的自然而然就知道了。
流程源码剖析
从代码中我们不难看出来,OKhttpClient,Request,Response这些都是比较重要的类。我们看OKhttpClient的构造方法 new OKhttpClient();
public OkHttpClient() {
this(new Builder());
}
复制代码
他的无参构造方法中调用了有Builder参数的构造方法,自己new 了一个Builder对象穿了进去。我们接着看new Builder()方法。
public Builder() {
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
...
...
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
pingInterval = 0;
}
复制代码
发现该构造方法中初始化了一些对象,赋值了变量。
OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
this.protocols = builder.protocols;
...
...
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);
}
}
复制代码
上面代码我们看到,把Builder的变量的值赋给了OKhttpClient,我省略的部分代码,不然代码量会比较多。 这种Builder建造者模式的设计在OKhttp项目中有很多,比如Request,Response,CacheControl等类,都采用了Builder模式来设计。比较灵活方便。
然后我们看Request.Builder.url(url)方法。
public Builder url(String url) {
if (url == null) throw new NullPointerException("url == null");
// Silently replace web socket URLs with HTTP URLs.
if (url.regionMatches(true, 0, "ws:", 0, 3)) {
url = "http:" + url.substring(3);
} else if (url.regionMatches(true, 0, "wss:", 0, 4)) {
url = "https:" + url.substring(4);
}
HttpUrl parsed = HttpUrl.parse(url);
if (parsed == null) throw new IllegalArgumentException("unexpected url: " + url);
return url(parsed);
}
复制代码
这个方法的作用,是对url进行的判断,转化。如果传进来的url字符串是一个无效的http,https URL,会出现IllegalArgumentException,为了避免这种情况,就出现了这个方法,通过调用HttpUrl.parse()方法来进行转化,如果是无效的http,https URL,会返回null,而不出现IllegalArgumentException。
通过上面的方法,把要请求的url设置进去之后,接下来就调用了client.newCall(request).enqueue(Callback)请求代码,我们看一下newCall(request)的源码:
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
复制代码
我们发现他是创建了一个RealCall对象,RealCall是实现Call接口,enqueue()方法是Call接口的方法,所以,运行enqueue()方法的是RealCall对象,所以我们来看RealCall对象里面的enqueue()方法。
okhttp里面的Call概念是一个将要被执行网络请求,可以被取消,只可以被执行一次。
@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().enqueue(new AsyncCall(responseCallback)); 这里我们发现,继续调用了Dispatcher的enqueue()方法,此刻OKhttp里面很重要的一个类出现在我们面前了,那就是Dispatcher(调度分配器)。其实之前在OKhttpClient对象创建的时候,就有dispatcher的身影了,不过这次是比较明显的看到 。。。
Dispatcher (调度分配器)
Dispatcher是一个主要在异步请求时会用到的一个策略,我看有的文章说的比较绝对,说只有在异步请求才会用到,其实同步也有用到,只不过没异步所占的份额大罢了。
Dispatcher内部维护了一个线程池,用来对Call网络请求,进行处理。每一个dispatcher可以最大并发64个请求,每一台主机可以最大并发5个。我们从下面代码可以看到:
public final class Dispatcher {
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
private @Nullable Runnable idleCallback;
/** Executes calls. Created lazily. */
private @Nullable ExecutorService executorService;
/** Ready async calls in the order they'll be run. */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
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;
}
复制代码
上面代码中还有一些通过名字就知道的变量,就不介绍了。我们主要看一下线程池的创键,这边在创建线程池的时候,没有用常用的四大线程池,他创建了一个核心线程数为0,最大线程数为Integer.MAX_VALUE,线程存活时间为1分钟,容器为SynchronizeQueue的这样一个线程池。
关于SynchronizeQueue的介绍可以看这里SynchronizeQueue内容 他这个线程池很像四大线程池中的newCacheThreadPool。适合处理请求频繁,高并发的任务。 这里的线程池,默认是这样的,如果有需求可以自己设置的。
Dispatcher如他名字的含义一样,调度。进他的源码可以看到,它里面的方法就是,执行请求,取消请求,结束请求等之类的调用,他的类里面的代码不多,大家可以自己进去看看,很容易理解。这里提一嘴,建议在看这篇文章的同学,采取边看文章,边看源码的方式来理解掌握。
回过头来继续看这句代码:
client.dispatcher().enqueue(new AsyncCall(responseCallback));
复制代码
我们知道他就是调用Dispatcher里面的enqueue方法,我们来看,Dispatcher里面的enqueue方法。
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
} }
复制代码
runningAsyncCalls是一个存储正在进行异步请求的容器,这边对它进行了判断,如果runningAsyncCalls的size小于最大并发量64,并且每台主机的正在请求量小于5(maxRequestsPerHost),runningAsyncCalls就把这个异步请求添加进去,接着用通过调用线程池中的线程去执行这个AsyncCall,否者就用readyAsyncCalls容器去存储该请求。这段逻辑很好理解。我们现在看一下AsyncCall。看看这个AsyncCall是什么。
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
复制代码
这里我们发现了之前传过来的Callback回调,AsyncCall是RealCall的内部类。AsyncCall 继承NamedRunnable ,咦。。有Runnable,难到。。。我们接着看NamedRunnable
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();
}
复制代码
果然~~,NameRunnable是实现了Runnable接口。是一个抽象类,在run方法中执行了execute()方法,execute方法是一个抽象方法。他的实现在NameRunnable子类中,AsyncCall是NameRunnable的子类。所以,我们就定位到了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);
}
}
复制代码
看到这里,我们要知道,之前Dispatcher里面的线程池执行的任务就是上面的execute()方法。然后根据上面的源码我们发现,在这里获取到了Response,网络响应结果。并且对根据条件,处理了对Callback回调的响应。
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
复制代码
之前我们在使用OKhttp进行一步请求的时候,传进去的Callback,结果回调,就是AsyncCall 里面execute()方法里面的Callback。
获取到Response的这行代码
Response response = getResponseWithInterceptorChain();
复制代码
我们待会再看。我们接着看,发现finally语句块中执行的下面的语句:
client.dispatcher().finished(this);
复制代码
调用了Dispatcher的finished方法。
/** 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();
}
}
复制代码
这里的promoteCalls参数,在异步请求的情况下,Boolean值为true,同步的话,值为false。我们看到finished方法里面有一个同步代码块,首先他移除了当前正在运行的call(请求都完成了,到这该结束),逻辑没毛病。然后获取正在执行的请求数量,如果数量==0,idleCallback不为零,就执行idleCallback的run方法。idleCallback默认为null的,用户有需要得设置了才有。咦,我们刚刚是不是错过了什么逻辑。对了,这行代码还没看呢。没事咱现在看。 if (promoteCalls) promoteCalls();
我们分析的是异步请求,所以promoteCalls是true。我们看promoteCalls()方法。
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.
}
}
复制代码
逻辑很简单,我就不细说了,就是把readyAsyncCalls准备执行里的请求放到runningAsyncCalls正在执行的容器里,并通过executorService().execute(call);来执行。
接下来我们来看之前被我们放在一边的获取Response的方法,代码如下:
Response response = getResponseWithInterceptorChain();
复制代码
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());//注意1
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());//注意2
}
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(拦截器)。
Interceptor(拦截器)
interceptor是什么?他是用来对发出去的网络请求和对应响应的responses 进行(Observes)观察,(modifies)修改和potentially short-circuits(可能的短路?掉线?不好翻译,理解吧),也就是对Request和Response进行修改,处理的一个借口。通常,interceptor(拦截器)会对Request和Response的Header进行add,remove,transform等操作。
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
interface Chain {
Request request();
Response proceed(Request request) throws IOException;
...
...
省略...
复制代码
我们现在来看上面的getResponseWithInterceptorChain()方法。他首先创建了一个ArrayList集合把一些xxxInterceptor添加进去,然后创建了RealInterceptorChain,并执行chain.proceed(originalRequest);方法。**这里先说一个结论就是interceptors容器里面的各式各样的interceptor拦截器会,按添加的顺序,逐个执行intercept()方法。**这个结论后续会被证明。然后我们上面代码,有两个注意点,那两个地方的interceptor是开发者,根据需要选择是否设置的。设置后执行顺序就是add的顺序。
接着我们看RealInterceptorChain.proceed(originalRequest);方法。
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// 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);
// Confirm that the intercepted response isn't null.
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}
if (response.body() == null) {
throw new IllegalStateException(
"interceptor " + interceptor + " returned a response with no body");
}
return response;
}
复制代码
这里,我删除了一些非关键代码,方便我们浏览。刚开始,index和calls的值都是0,我们看到他创建了一个RealInterceptorChain类型的next对象,他的构造方法这里注意一下,他的构造方法传进去的参数,有一个index+1,也就是说这个RealInterceptorChain的index值+1了。变成了1 注意这里是新创建的next对象里面的index值加一了,并不是说现在在所的对象里面index加一,Interceptor interceptor = interceptors.get(index);在执行该语句时,index还是0,这里如果开发者通过调用OKhttpClient的addInterceptor(Interceptor interceptor)方法,设置了拦截器,则会先执行用户通过该方法设置的拦截器,否则就是执行RetryAndFollowUpInterceptor 拦截器。这个拦截器看名字我们很容易知道,他是失败重试和重定向的时候发挥作用的,这里我们先不细说了。本篇文章的主要目的是捋清流程。我们接着看,这里我们没有add 自定义的拦截器,所以就执行了RetryAndFollowUpInterceptor 拦截器的interceptor.intercept(next);方法,注意这里把next对象,穿进去了,还有要记得我们之前强调的,next对象里面的index的值加一了已经,变成了1了。
然后我们看RetryAndFollowUpInterceptor 拦截器的interceptor.intercept(next);方法。
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;//注意1
Call call = realChain.call();
EventListener eventListener = realChain.eventListener();
streamAllocation = new StreamAllocation(client.connectionPool(), createAddress(request.url()),
call, eventListener, callStackTrace);
int followUpCount = 0;
Response priorResponse = null;
while (true) {
if (canceled) {
streamAllocation.release();
throw new IOException("Canceled");
}
Response response;
boolean releaseConnection = true;
try {
response = realChain.proceed(request, streamAllocation, null, null);//注意2
releaseConnection = false;
复制代码
上面的代码,别的我们先不看,我们主要看我上面标注的注意1,和注意2处的代码。看注意1,这里传进来的chain就是next对象,然后注意2,那边realChain.proceed()方法,(⊙o⊙)哦~有点熟悉哦,我们进去看一下。
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// 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);
// Confirm that the intercepted response isn't null.
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}
if (response.body() == null) {
throw new IllegalStateException(
"interceptor " + interceptor + " returned a response with no body");
}
return response;
}
复制代码
发现又走了回来,注意,此时index的值不再是0了,此时变成了1,接着就是上面一样的逻辑,创建新的next对象,index加1,从interceptors容器中取出下一个interceptor拦截器,执行intercept(next)方法。正是这种递归式的调用,来吧interceptors集合里面的所有拦截器都跑一边,最后获取到Response对象。
总结
不自觉中说了这么多,之后的文章会对Interceptor的各个实现类,CacheControl等重要类进行剖析。最后上一张总结图: