费话不多说,先上代码
final OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder()
.url("https://www.baidu.com")
.build();
Response response = okHttpClient.newCall(request).execute();
这是一个同步get请求的代码。代码包含了三个步骤:
1、创建okhttpClient;
2、创建所需的请求信息(request对象);
3、通过okhttpClient来执行该请求。
下面就对以上三个步骤的源码进行分析。
创建okhttpClient
OkHttpClient通过建造者模式来创建对象:使用静态内部类Builder对象的参数来为OkHttpClient对象自身的参数进行赋值。
public OkHttpClient() {
this(new Builder());
}
OkHttpClient(Builder builder) {
//通过builder中参数为自身的参数赋值
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
this.protocols = builder.protocols;
this.connectionSpecs = builder.connectionSpecs;
...
}
OkHttpClient.Builder对象的构造器中会对参数赋初始值,源码如下:
public Builder() {
dispatcher = new Dispatcher(); //请求调度器,通过线程池来对异步请求进行管理
protocols = DEFAULT_PROTOCOLS;//http协议,默认支持TTP2.X和HTTP1.X
connectionSpecs = DEFAULT_CONNECTION_SPECS;//对socket连接进行配置的类,包含URL地址,TLS版本和一些加密信息
eventListenerFactory = EventListener.factory(EventListener.NONE);//请求监听器
proxySelector = ProxySelector.getDefault();
...
connectionPool = new ConnectionPool();//默认创建一个连接池,多个OkHttpClient实例会导致创建多个线程池对象。
dns = Dns.SYSTEM;
followSslRedirects = true;
followRedirects = true;//默认支持重定向
retryOnConnectionFailure = true;//默认支持失败重连
callTimeout = 0;
connectTimeout = 10_000;//连接超时默认10秒
readTimeout = 10_000;//读取超时
writeTimeout = 10_000;//写超时
pingInterval = 0;
}
有时OkHttp的默认配置不能满足我们的需求,例如我们需要将读取超时时间设为30秒并且禁止失败重连。那我们需要通过Builder的build()方法来创建OkHttpClient对象。
final OkHttpClient okHttpClient = new OkHttpClient.Builder()
.readTimeout(10L, TimeUnit.SECONDS)
.retryOnConnectionFailure(false)
.build();
当我们对Buider对象配置完需要修改的参数之后,通过build()方法来创建OkHTTPClient对象。
//OkHttpClient静态内部类
public static final class Builder {
...
public Builder readTimeout(long timeout, TimeUnit unit) {
readTimeout = checkDuration("timeout", timeout, unit);
return this;
}
...
public Builder retryOnConnectionFailure(boolean retryOnConnectionFailure) {
this.retryOnConnectionFailure = retryOnConnectionFailure;
return this;
}
...
// 配置完成之后通过此方法创建OkHttpClient对象
public OkHttpClient build() {
return new OkHttpClient(this);
}
}
okhttp建议我们只创建一个OkHttpClient实例并在整个应用中使用它(这是因为多个实例会导致创建多个连接池对象、多个调度器以及多个缓存,浪费内存不说还有可能因为多个chache使用同一个缓存目录导致crash)。而我们的参数配置(如超时、失败重连、拦截器等)是在创建OKHttpClient对象的时候设置的。如果要为某个请求单独配置参数,则需要通过OkHttpClient的newBuilder()方法来创建一个与原始client实例共享连接池、调度器和配置的builder对象。
//原始创建的okhttpClient 对象
final OkHttpClient okHttpClient = new OkHttpClient.Builder()
.readTimeout(20L, TimeUnit.SECONDS)
.retryOnConnectionFailure(false)
.build();
//某个请求所需的okhttpClient对象
OkHttpClient otherClient = okHttpClient.newBuilder()
.readTimeout(30L, TimeUnit.SECONDS)
.build();
源码如下,当调用newBuilder()方法时,会将当前okhttpClient对象(也就是原始的实例对象)传递给Builder的构造器,那我们创建的builder对象则和原始实例对象共用相同的参数。
public Builder newBuilder() {
return new Builder(this); //将原始的client对象传递给构造器
}
Builder(OkHttpClient okHttpClient) {
//新创建的builder对象和原始的client对象使用相同的参数,我们只需将我们所需要改变的参数设置后即可
this.dispatcher = okHttpClient.dispatcher;
this.proxy = okHttpClient.proxy;
this.protocols = okHttpClient.protocols;
this.connectionSpecs = okHttpClient.connectionSpecs;
...
}
以上就是创建OkHttpClient对象的源码分析,总结如下:
- OkHttpClient对象的创建使用的是建造者设计模式
- 通过new OkHttpClient()方式创建默认配置的对象
- 通过new OkHttpClient.Builder()来修改配置然后通过build()方法创建对象
- 通过OkHttpClient的newBuilder()方法来创建与原始实例对象共享连接池、调度器和配置的Builder对象,通过修改builder对象的部分参数来创建满足单个请求所需要的特殊OkHttpClient对象.
创建请求信息对象(request对象)
Request对象中包含一个URL地址、一个method(请求方法名称)、一个Headers(请求头对象)、一个RequestBody(请求体对象)。Request对象的创建同样是使用的建造者模式,对于请求对象来说url地址是必要的参数,所以request并没有提供默认构造器供我们调用。Request源码稍微简单点,源码如下:
public static class Builder {
public Builder() {
this.method = "GET";
this.headers = new Headers.Builder();
}
public Request build() {
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
}
}
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);
}
从上面代码可知,默认会指定请求方法为get方法。通过Builder的build方法创建了我们的request对象。
Header内部以字符串数组的形式保存键值对数据。但操作类似于MultiMap。当我们添加请求头时,可以通过header()或者addHeader()方法来添加。从源码可以很直观的看出两者的区别。
public final class Headers {
private final String[] namesAndValues;//使用数组保存请求头信息
}
//header方法会保证键值对一一对应
public Builder header(String name, String value) {
headers.set(name, value);
return this;
}
public Builder set(String name, String value) {
checkName(name);
checkValue(value, name);
removeAll(name);//移除所有name对应的value值
addLenient(name, value);//将新value值添加进去
return this;
}
//遍历数组删除name,以及该name对应的值
public Builder removeAll(String name) {
for (int i = 0; i < namesAndValues.size(); i += 2) {
if (name.equalsIgnoreCase(namesAndValues.get(i))) {
namesAndValues.remove(i); // name
namesAndValues.remove(i); // value
i -= 2;
}
}
return this;
}
//现将键name添加进去,然后将值value添加进去,保证了偶数位都为键,基数为都为值。
Builder addLenient(String name, String value) {
namesAndValues.add(name);
namesAndValues.add(value.trim());
return this;
}
//addHeader允许一个键对应多个值
public Builder addHeader(String name, String value) {
headers.add(name, value);
return this;
}
public Builder add(String name, String value) {
checkName(name);
checkValue(value, name);
return addLenient(name, value);//将值添加到键对应的集合中
}
当我们使用post方式提交数据的时候,数据都是保存在RequestBody(方法体)中的。RequestBody是一个抽象类,可以通过RequestBody 提供的静态方法create()创建。
public static RequestBody create(final @Nullable MediaType contentType, final byte[] content,
final int offset, final int byteCount) {
if (content == null) throw new NullPointerException("content == null");
Util.checkOffsetAndCount(content.length, offset, byteCount);
//直接返回一个匿名内部类对象
return new RequestBody() {
@Override public @Nullable MediaType contentType() {
return contentType;//数据类型 指定非空
}
@Override public long contentLength() {
return byteCount;
}
@Override public void writeTo(BufferedSink sink) throws IOException {
sink.write(content, offset, byteCount);//写入数据
}
};
}
从源码可知,创建一个RequestBody对象,需要指定他的数据类型(MediaType)。数据以字节数组(这样就可以传输文件、照片等数据)的形式通过BufferedSink写入。
创建Request对象总结如下:
- Request对象的创建使用的是建造者模式
- Request类内部包含url地址、方法名称method、请求头信息headers以及请求体requestBody
- 请求头信息headers的数据以数组形式存放,支持一对一(通过header()方法添加),一对多(通过addHeader()方法添加)的键值对。
- 请求体是一个抽象类,可以通过RequestBody的静态方法create()来创建requestBody对象。
执行请求
okHttpClient.newCall(request).execute();
首先通过OkHttpClient的newCall()方法创建一个call对象(call是一个接口,它的实现是RealCall)。
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
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.transmitter = new Transmitter(client, call);
return call;
}
RealCall类代码如下,realCall对象持有okhttpClient、request对象的引用。
final class RealCall implements Call {
final OkHttpClient client;
private Transmitter transmitter;
final Request originalRequest;
final boolean forWebSocket;
private boolean executed;//是否已经执行
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
}
}
对于同步请求来说,通过realCall对象的excute()方法执行该请求。excute()方法中显示调用了dispatcher的excuted()方法,接着调动核心方法getResponseWithInterceptorChain遍历拦截器获取响应,最后调用dispatcher的finished方法结束请求。
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
transmitter.timeoutEnter();
transmitter.callStart();
try {
client.dispatcher().executed(this);
return getResponseWithInterceptorChain();
} finally {
client.dispatcher().finished(this);
}
}
okhttpClient的dispatcher()方法直接返回一个Dispatcher(请求调度器对象)。Dispatcher内部维护着三个请求队列和一个线程池对象,源码如下。
public final class 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<>();//正在执行的同步请求队列
}
接下来看看Dispatcher的excute()方法,直接将同步请求的call对象添加到同步请求队列中。
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
下面看看核心方法getResponseWithInterceptorChain()的源码。
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(new RetryAndFollowUpInterceptor(client));
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, transmitter, null, 0,
originalRequest, this, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
boolean calledNoMoreExchanges = false;
try {
Response response = chain.proceed(originalRequest);
if (transmitter.isCanceled()) {
closeQuietly(response);
throw new IOException("Canceled");
}
return response;
} catch (IOException e) {
calledNoMoreExchanges = true;
throw transmitter.noMoreExchanges(e);
} finally {
if (!calledNoMoreExchanges) {
transmitter.noMoreExchanges(null);
}
}
}
方法内部拆分如下:
- 第一步,将一系列拦截器添加到拦截器集合当中。添加顺序为:自定义的应用拦截器,okhttp定义的默认拦截器,自定义的网络拦截器,CallServerInterceptor(网络请求拦截器)
- 第二步,通过拦截器集合构造一个拦截器链对象RealInterceptorChain
- 第三步,通过拦截器器链RealInterceptorChain的proceed()方法递归调用拦截器获得相应response
对于第一步骤,okhttp添加的默认拦截器如下
//用于失败重连,请求跟踪
interceptors.add(new RetryAndFollowUpInterceptor(client));
//用于添加一些默认请求头信息
interceptors.add(new BridgeInterceptor(client.cookieJar()));
//用于配置缓存信息
interceptors.add(new CacheInterceptor(client.internalCache()));
//用于获取网络连接
interceptors.add(new ConnectInterceptor(client));
//真正的执行网络请求获取响应response
interceptors.add(new CallServerInterceptor(forWebSocket));
对于第二步骤,创建拦截器连对象代码如下,这里注意创建时index传的是0
Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
originalRequest, this, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
public RealInterceptorChain(List<Interceptor> interceptors, Transmitter transmitter,
@Nullable Exchange exchange, int index, Request request, Call call,
int connectTimeout, int readTimeout, int writeTimeout) {
this.interceptors = interceptors;
this.transmitter = transmitter;
this.exchange = exchange;
this.index = index;
this.request = request;
this.call = call;
this.connectTimeout = connectTimeout;
this.readTimeout = readTimeout;
this.writeTimeout = writeTimeout;
}
对于第三步骤,看看proceed()的源码
public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
...
//构造新的拦截器链对象
RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
//获得拦截器集合中的第index位置的对象
Interceptor interceptor = interceptors.get(index);
//调用interceptor的intercept方法
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;
}
从上述代码可知会首先将interceptor集合中的第一个拦截器取出来并调用他的intercept()方法。我们以RetryAndFollowUpInterceptor为例来看看他的intercept()方法。
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Transmitter transmitter = realChain.transmitter();
...
try {
response = realChain.proceed(request, transmitter, null);
success = true;
} catch (RouteException e) {
// The attempt to connect via a route failed. The request will not have been sent.
if (!recover(e.getLastConnectException(), transmitter, false, request)) {
throw e.getFirstConnectException();
}
continue;
} catch (IOException e) {
// An attempt to communicate with a server failed. The request may have been sent.
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
if (!recover(e, transmitter, requestSendStarted, request)) throw e;
continue;
} finally {
// The network call threw an exception. Release any resources.
if (!success) {
transmitter.exchangeDoneDueToException();
}
}
}
在intercept()方法内部又会去调用realChain.proceed()方法,通过index+1,获取到intercept集合中的下一个拦截器对象。继而递归调用所有拦截器的intercept()方法,最后调用到CallServerInterceptor的intercept方法,在CallServerInterceptor中会真正发起网络请求获取响应返回。
到这里整个getResponseWithInterceptorChain()方法执行完毕并返回响应response信息。接着回到realCall的excute()方法,在finally语句快中会调用dispatcher的finish方法。
//同步请求finish()方法,将同步请求队列和请求call交给私有方法finished()处理
void finished(RealCall call) {
finished(runningSyncCalls, call);//调用内部私有方法finished()
}
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;
}
//从异步等待队列中取出call放入异步执行队列中去执行(针对于异步请求)
boolean isRunning = promoteAndExecute();
if (!isRunning && idleCallback != null) {
idleCallback.run();
}
}
以上分析了okhttp同步请求excute()方法的源码,接下来看看异步请求enqueue()方法。异步请求会调用realCall的enqueue()方法。
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
transmitter.callStart();
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
内部调用了dispatcher的enqueue方法,传递对象为AsyncCall,AsyncCall是一个Runnable对象。
void enqueue(AsyncCall call) {
synchronized (this) {
readyAsyncCalls.add(call);
if (!call.get().forWebSocket) {
AsyncCall existingCall = findExistingCallWithHost(call.host());
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
}
}
promoteAndExecute();
}
dispatcher的enqueue方法中,直接将请求对象AsyncCall添加到待执行的队列中,然后调用promoteAndExecute()方法。
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 (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.
i.remove();
asyncCall.callsPerHost().incrementAndGet();
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;
}
promoteAndExecute()方法逻辑是这样的:首先遍历待执行的异步请求队列readyAsyncCalls,如果当前正在执行的异步请求数量(runningAsyncCalls.size())没有达到最大值,并且每个主机的请求最大值也没打到,则将该异步请求添加到请求集合executableCalls 中。接着遍历executableCalls中的AsyncCall 对象调用它的executeOn()方法执行网络请求。
void executeOn(ExecutorService executorService) {
assert (!Thread.holdsLock(client.dispatcher()));
boolean success = false;
try {
executorService.execute(this);
success = true;
} catch (RejectedExecutionException e) {
InterruptedIOException ioException = new InterruptedIOException("executor rejected");
ioException.initCause(e);
transmitter.noMoreExchanges(ioException);
responseCallback.onFailure(RealCall.this, ioException);
} finally {
if (!success) {
client.dispatcher().finished(this); // This call is no longer running!
}
}
}
这里要说明一下AsyncCall是一个Runnable对象,他继承自NamedRunnable,NamedRunnable实现了Runnable接口。线程池执行Runnable对象时会去调用它的run()方法,而在NamedRunnable的run方法中调用了excute()方法,所以最终executorService.execute(this);会触发AsyncCall对象的excute()方法被调用。
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();
}
最后看看AsyncCall的excute()方法
@Override protected void execute() {
boolean signalledCallback = false;
transmitter.timeoutEnter();
try {
Response response = getResponseWithInterceptorChain();
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 {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
可知同样调用了getResponseWithInterceptorChain()方法去递归调用拦截器并最终返回response对象,最后调用callback的onresponse方法回调结束本次异步请求。
网友评论