开篇陈述:OkHttp做为一个优秀的网络请求库,陪咱们这些Android开发佬走过了许多风风雨雨的夜晚,因此今天特写一篇深刻理解做文。整个系列篇中,在okhttp3.14.0版本上,依照OkHttp的使用为引线,作结构以下剖析:java
public static final MediaType JSON
= MediaType.get("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(json, JSON);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
//见【2.2】client.newCall()
//见【2.3】RealCall.execute()
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
复制代码
如上是一次同步的网络请求,主要作以下事情:web
OkHttpClient.java
@Override public Call newCall(Request request) {
//直接调用Realcall的方法,新建了一个RealCall对象。
return RealCall.newRealCall(this, request, false /* for web socket */);
}
RealCall.java
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
RealCall call = new RealCall(client, originalRequest, forWebSocket);
call.transmitter = new Transmitter(client, call);
return call;
}
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
//这里须要特别注意OkHttpClient对象被Call持有。
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
}
复制代码
如上所示,OkHttpClient.newCall() 的调用链最终构建了一个 RealCall 对象,而且把client做为 RealCall 的成员变量,方便后续请求从 client 获取配置。json
RealCall.java
@Override public Response execute() throws IOException {
synchronized (this) {
// 若是该请求已经执行过,报错。
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
transmitter.timeoutEnter();
transmitter.callStart();
try {
//见【2.4】获取 client 里面的调度器 Dispatcher 并记录这个请求。
client.dispatcher().executed(this);
//见【2.5】经过责任链的方式将发起请求并返回结果。这里是同步动做,会阻塞。
return getResponseWithInterceptorChain();
} finally {
//请求完后须要把这个请求从调度器中移走
client.dispatcher().finished(this);
}
}
复制代码
Call在被执行时作以下事情:设计模式
Dispatcher.java
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */ private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>(); synchronized void executed(RealCall call) { runningSyncCalls.add(call); } 复制代码
就是将这次请求加入到一个双端队列数据集合中。缓存
Response getResponseWithInterceptorChain() throws IOException {
// 将请求的具体逻辑进行分层,并采用责任链的方式进行构造。
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());
}
// 网络请求拦截器,真正网络通行的地方,这个拦截器处理事后会生成一个Response
interceptors.add(new CallServerInterceptor(forWebSocket));
//依照如上配置,构建出一个请求的处理逻辑责任链,特别注意:这条链开始于下标位置为的0拦截器。
Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
originalRequest, this, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
boolean calledNoMoreExchanges = false;
try {
//详见【2.6】,按下处理逻辑链条的开关。
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);
}
}
}
复制代码
经过该方法的名字咱们也能够知道,这个方法就是经过拦截器链来得到Response的过程。他作了以下事情:bash
【2.6】RealInterceptorChain.proceed()cookie
RealInterceptorChain.java
public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
...
/**
* index+1:构建出新的拦截链,不过新的拦截链的处理拦截器是下标为index+1的
* 实现了责任链中,处理逻辑的流转。
*/
RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
//此时index = 0;因此拿到了第一个拦截器,而且调用他的intercept 方法进行具体逻辑处理。
Interceptor interceptor = interceptors.get(index);
//当前拦截器对网络请求进行处理。
Response response = interceptor.intercept(next);
// Confirm that the next interceptor made its required call to chain.proceed().
if (exchange != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
// 省略对response合法性的检查代码
...
return response;
}
复制代码
总结:网络
3.1app
public static final MediaType JSON
= MediaType.get("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(json, JSON);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
//client.newCall():同【2.2】
//详见【3.2】RealCall.enqueue()
try (Response response = client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
}) {
return response.body().string();
}
}
复制代码
如上是一次OkHttp的异步请求使用方法,基本于【2.1】的同步请求一致,惟一不一样的是,call的异步调用是经过RealCall.enqueue()实现的。而请求结果经过Callback回调到主线程。异步
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
transmitter.callStart();
//详见【3.4】:AsyncCall()
//详见【3.5】:Dispatcher.enqueue()
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
复制代码
将用户建立的callback做为参数传入AsyncCall()构造函数。AsyncCall 继承于Runnable.
AsyncCall.java
final class AsyncCall extends NamedRunnable {
private volatile AtomicInteger callsPerHost = new AtomicInteger(0);
...
/**
* 该方法是在dispather须要执行此请求的时候,分配给它线程池,此异步请求便在这个线程池中执行网络请求。
*/
void executeOn(ExecutorService executorService) {
...
boolean success = false;
try {
//异步的关键:将请求放到线程池中执行。
executorService.execute(this);
success = true;
} catch (RejectedExecutionException e) {
...
success = false;
} finally {
if (!success) {
client.dispatcher().finished(this); // 执行失败会经过Dispatcher进行finished,之后不再会用此AsyncCall。
}
}
}
@Override protected void execute() {
boolean signalledCallback = false;
transmitter.timeoutEnter();
try {
//此处同【2.5】
Response response = getResponseWithInterceptorChain();
signalledCallback = true;
//请求成功时,回调Response给到用户
responseCallback.onResponse(RealCall.this, response);
} catch (IOException e) {
...
//请求错误时,回调错误接口给到用户
responseCallback.onFailure(RealCall.this, e);
} finally {
//详见【3.6】,结束一次请求。
client.dispatcher().finished(this);
}
}
}
复制代码
从上面能够看出,AsyncCall继承于Runnable,它提供了将Call放到线程池执行的能力,实现了请求的异步流程。
Dispatcher.java
//准备进行异步调用的请求。
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
//正在执行的异步请求。
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
void enqueue(AsyncCall call) {
synchronized (this) {
//将异步请求加入到双端队列中
readyAsyncCalls.add(call);
// 寻找是否有同Host的请求,若是有进行复用
if (!call.get().forWebSocket) {
AsyncCall existingCall = findExistingCallWithHost(call.host());
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
}
}
//【详见3.7】将符合条件的Ready的异步请求转入runningAsyncCalls,并执行
promoteAndExecute();
}
复制代码
总结:
Dipatcher.java
private <T> void finished(Deque<T> calls, T call) {
Runnable idleCallback;
synchronized (this) {
...
//【详见3.7】一个请求完成后,检查此时是否有在等待执行的请求,并处理。
boolean isRunning = promoteAndExecute();
if (!isRunning && idleCallback != null) {
//通知此时已经没有异步请求任务
idleCallback.run();
}
}
复制代码
总结:调度器结束一次请求
private boolean promoteAndExecute() {
...
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;
if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue;
//知足条件,便把预备队的请求提高到执行队列。
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);
//【详见3.4】
asyncCall.executeOn(executorService());
}
return isRunning;

}
复制代码
总结 :该方法是对预备队列里的请求提高至执行队列并执行的一次尝试。若是不能执行,他启动时机将会延后到其余请求结束(如【3.6】逻辑)。
小篇结:本篇中以Okhttp的用法为主线,探究了它的同步请求、异步请求的代码逻辑。而OkHttp最主要的设计模式:责任链模式也在其中有涉及到。最后,咱们经过2张图片来理解同步和异步的整个请求流程(图片不是本人画的):
同步请求
复制代码
异步请求流程复制代码