创建Client的外部调用
OKHttpClient client = new OkHttpClient.Builder().readTimeOut(5,TimeUnit.SECONDS).build();
先来看下OkHttpClient的内部类Builder的构造方法:
public Builder() {
dispatcher = new Dispatcher(); //okhttp请求的分发器,由它决定异步请求是直接处理,还是进行缓存等待。
//当然,他对同步请求并没有做太多操作,只是把它的同步请求放到队列中去执行。
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
eventListenerFactory = EventListener.factory(EventListener.NONE);
proxySelector = ProxySelector.getDefault();
if (proxySelector == null) {
proxySelector = new NullProxySelector();
}
cookieJar = CookieJar.NO_COOKIES;
socketFactory = SocketFactory.getDefault();
hostnameVerifier = OkHostnameVerifier.INSTANCE;
certificatePinner = CertificatePinner.DEFAULT;
proxyAuthenticator = Authenticator.NONE;
authenticator = Authenticator.NONE;
connectionPool = new ConnectionPool();//它其实是一个连接池,我们可以这样理解,客户端和服务端之间的连接,
//我们可以把它抽象为一个connection,
//而每一个connection,我们都会把它放在connectionPool这个连接池当中,他来进行一个统一的管理,
//当你请求到的url是相同的时候,你就可以选择复用,这是作用之一;
//另一个作用就是connectionPool这个类它实现了哪一些网络连接它给保持打开状态,哪一些是可以用来复用的,
//这些策略的设置,也是需要connectionPool这个连接池来进行管理的。
dns = Dns.SYSTEM;
followSslRedirects = true;
followRedirects = true;
retryOnConnectionFailure = true;
callTimeout = 0;
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
pingInterval = 0;
}
这里这么多参数在后面的一些请求流程中都会使用到,这就是Builder创建对象的模式,用Builder对象来封装OkHttp初始化所需要的参数,然后传递我们的Builder对象到OkHttpClient的构造方法里,完成它的整个对象属性的初始化。
当你创建一个对象时,如果这个对象需要很多的参数,这时候,你可以使用到Builder这个创建模式,是一个很好的解决办法。
创建Request请求报文信息类
Request的创建方式也是通过Builder来创建的,然后,通过链式调用,给Request指定url,或者头部,以及方法等等。
外部调用如下:
Request request = new Request.Builder().url("http://www.baidu.com").get().build();
先来看下Request的内部类Builder的构造方法。
public Builder() {
this.method = "GET";//指定了请求方式为GET,它是默认的
this.headers = new Headers.Builder();//创建了Headers的Builder对象来保存它的头部信息
}
接下来看下build()方法,在build()方法里面,他是直接创建一个Request对象,然后把当前的build对象传递过去,它的意思是把之前配置好的请求方式,url,头部,都赋值给Request对象。
public Request build() {
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
}
还有Request的构造方法。Request构造方法就是为其指定了请求方式,以及请求的网络地址url,以及它的头部信息等等,这样就完成了OkHttp同步请求的前两步。
Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build();
this.body = builder.body;
this.duplex = builder.duplex;
this.tags = Util.immutableMap(builder.tags);
}
创建Call对象
外部调用:
Call call = client.newCall(request);
它是通过client的newCall来进行实现的。
/**
* Prepares the {@code request} to be executed at some point in the future.
* Call类是一个接口
* Call类是一个准备执行的请求,可以被取消,代表着一个单一的请求/响应流,不能被执行两次。
* New一个Call的子类RealCall去执行execute方法
*/
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
由于Call是一个接口,所以它的实际操作都是在它实现类RealCall中所做的,下面来看一下RealCall中是怎么做的。
下面来看下RealCall.java中是怎么做的。
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
// Safely publish the Call instance to the EventListener.
RealCall call = new RealCall(client, originalRequest, forWebSocket);
call.eventListener = client.eventListenerFactory().create(call);
return call;
}
首先,它先创建了它的实现类RealCall对象,然后,还赋值了一个Listener,就返回了。
它究竟做了哪些工作呢?我们到RealCall 的构造方法中看一下。
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
this.timeout = new AsyncTimeout() {
@Override
protected void timedOut() {
cancel();
}
};
this.timeout.timeout(client.callTimeoutMillis(), MILLISECONDS);
}
RealCall其实是持有了前两步初始化好的okHttpClient,originalRequest,同时,还赋值了一个重定向拦截器。
不管是同步,还是异步请求,它都是调用了client.newCall这个方法来进行创建的,然后我们通过创建好的Call对象进行操作。
使用execute完成同步请求
Response response = call.execute();
看看Call.java的execute()方法做了哪些操作。
/**
* Invokes the request immediately, and blocks until the response can be processed or is in
* error.
*
* <p>To avoid leaking resources callers should close the {@link Response} which in turn will
* close the underlying {@link ResponseBody}.
*
* <pre>{@code
*
* // ensure the response (and underlying response body) is closed
* try (Response response = client.newCall(request).execute()) {
* ...
* }
*
* }</pre>
*
* <p>The caller may read the response body with the response's {@link Response#body} method. To
* avoid leaking resources callers must {@linkplain ResponseBody close the response body} or the
* Response.
*
* <p>Note that transport-layer success (receiving a HTTP response code, headers and body) does
* not necessarily indicate application-layer success: {@code response} may still indicate an
* unhappy HTTP response code like 404 or 500.
*
* @throws IOException if the request could not be executed due to cancellation, a connectivity
* problem or timeout. Because networks can fail during an exchange, it is possible that the
* remote server accepted the request before the failure.
* @throws IllegalStateException when the call has already been executed.
*/
Response execute() throws IOException;
execute()是一个接口,再来看下它的具体实现,这是RealCall所做的具体实现。
/*
同步方法,不能在UI线程执行
*/
@Override
public Response execute() throws IOException {
synchronized (this) {
//是否执行过,如果执行过,抛出异常
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
timeout.enter();
eventListener.callStart(this);
try {
//调用Dispatcher的executed方法将该Call加入到一个双端队列里
client.dispatcher().executed(this);
//返回本次的Response对象
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 {
//从已经执行的双端队列中移除本次Call
client.dispatcher().finished(this);
}
}
在这其中的同步代码块中,它首先判断execute这个标志是否为true,它的意思是同一个http请求只能执行一次,没有执行过,他就会在下面把execute置为true,如果执行过,他就会抛出这个异常。
synchronized (this) {
//是否执行过,如果执行过,抛出异常
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
接下来,他会捕捉一些Http请求的异常堆栈信息。
captureCallStackTrace();
然后,它会开启了一个监听事件,每当我们的call调用execute方法,或者enqueue方法,就会开启这个Listener。
eventListener.callStart(this);
调用dispatcher()只是做了一个返回dispatcher对象的操作,dispatcher就是我们请求的分发器。
//调用Dispatcher的executed方法将该Call加入到一个双端队列里
client.dispatcher().executed(this);
继续进入这个executed方法。同步请求,它其实通过execute方法把它添加到队列之中,完成了这个操作。
(在okhttp3-master/app/src/main/java/okhttp3/Dispatcher.java)
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
关于Dispatcher,它的作用就是维持Call请求发给它的状态,同时,它也维护了一个线程池,用于执行网络请求,而Call这个请求在执行任务的时候,通过Dispatcher这个分发器类,把它的任务推到执行队列当中,然后来进行操作。
executed()方法主要是将我们的请求加入到同步请求队列当中,这个同步请求队列是在哪里定义的呢?它其实是在Dispatcher.java中定义的。
//正在运行的同步任务,包含取消尚未完成的调用
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
继续接下来execute()的同步方法,再获取response,这个其实是拦截器链的方法,在这个类内部会一次调用拦截器进行操作。
//返回本次的Response对象
Response result = getResponseWithInterceptorChain();
往下走,在finally{}里,它会主动地回收它的某些同步请求。
finally {
//从已经执行的双端队列中移除本次Call
client.dispatcher().finished(this);
}
进入finished方法看看,在Dispatcher.java里面。
/** Used by {@code AsyncCall#run} to signal completion. */
void finished(AsyncCall call) {
finished(runningAsyncCalls, call);
}
它又调用了另外一个Finish方法,把正在执行的同步请求传入进来。
private <T> void finished(Deque<T> calls, T call) {
Runnable idleCallback;
synchronized (this) {
//移除本次call。
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
idleCallback = this.idleCallback;
}
boolean isRunning = promoteAndExecute();
if (!isRunning && idleCallback != null) {
idleCallback.run();
}
}
它首先做的操作就是把队列中移除这个请求,如果不能移除,就抛出异常。
synchronized (this) {
//移除本次call。
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
idleCallback = this.idleCallback;
}
表示Dispatcher分发器中没有可运行的请求了,才可调用run方法。
if (!isRunning && idleCallback != null) {
idleCallback.run();
}
关于请求的判断在这个方法里。
private boolean promoteAndExecute() {
assert (!Thread.holdsLock(this));
List<AsyncCall> executableCalls = new ArrayList<>();
boolean isRunning;
synchronized (this) {
//循环判断准备请求队列是否还有请求
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall asyncCall = i.next();
//如果执行请求的队列数量大于等于,中止循环
if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
//小于最大主机请求限制
if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // Host max capacity.
//从准备队列里移除自身。
i.remove();
executableCalls.add(asyncCall);
//添加到执行队列中。
runningAsyncCalls.add(asyncCall);
}
isRunning = runningCallsCount() > 0;
}
//对新添加的任务尝试进行异步调用。
for (int i = 0, size = executableCalls.size(); i < size; i++) {
AsyncCall asyncCall = executableCalls.get(i);
asyncCall.executeOn(executorService());
}
return isRunning;
}
在同步请求中,dispatcher分发器做的事情非常简单,一是保存同步请求;二是移除同步请求。