okhttp流程
okhttp由okhttpclient开始,call的实现为realcall,通过request发送的请求以及realcall共同实现网络请求,现在我们一起来看下realcall以及其下的重点
realcall
realcall有两步最重要的,分别是呼叫disptcher.enqueue()以及getresponsewithInterceptorChain()两个方法
@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);
}
}
}
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);
}
然后我们可以先深入disptcher看看
dispatcher详解
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<>();
首先从成员变量来看,他维护了一个线程池,三个队列。
-
readyAsyncCalls队列是用于正在准备中的call
-
runningAsyncCalls队列用于放入线程池中执行的call
-
runningSyncCalls队列用于同步的call 这三个队列都是arraydeque(下节再讲,可以极大的提升数组插入的效率
synchronized void enqueue(AsyncCall call) { if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) { runningAsyncCalls.add(call); executorService().execute(call); } else { readyAsyncCalls.add(call); } }
异步执行便会调用enqueue()方法,这时会将该call放入线程池中执行同时加入runningAsyncCalls队列中。 而我们再看一下这个call是什么(AsyncCall)
final class AsyncCall extends NamedRunnable
可以看到AsyncCall其实就是runnable,所以我们传进去的就是一个由线程所持有的call。而一边制造线程,一遍消耗,这种模式就是生产者消费者模式
拦截器
拦截器运用到的是责任链模式,拦截器的启动在于这个方法
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);
return chain.proceed(originalRequest);//从RealCall的Request开始链式处理
}
分层设计
在真实的网路中,分为osi七层模型,这样层与层之间相互依赖,降低了开发和维护的难度。
okhttp爷采用分层设计的思想,这种从网络层到应用层很适合用责任链模式来实现,每一层interceptor输入都是Request,输出都是response,然后可以一层层的向下传递,每层取出自己要用的数据
而okhttp拦截器的层次如下:
- 自定义应用拦截器
- 重试、重定向拦截器
- 应用/网络桥接拦截器
- 缓存拦截器
- 连接拦截器
- 自定义网络拦截器
- 在线网络请求拦截器
开发者可以添加两种自定义Interceptor,一种是client.interceptors()应用层拦截器,一种是client.networkInterceptors()网络层拦截器。 但其实这两种都是Interceptor,为什么可以分成是应用层和网络层呢?
因为在网络层拦截器上方,是ConnectionInterceptor连接拦截器,这个拦截器里会提供Address、ConnectionPool等资源,可以用于处理网络连接,networkInterceptors是添加在这之后的,可以参与真正的网络层数据的处理。
重试、重定向拦截器
OkHttp正是把这个拦截器作为真正的入口,创建StreamAllocation对象,在StreamAllocation对象中准备了网络连接的Address、连接池等资源,后续的拦截器,使用的都是这个StreamAllocation对象。
网络环境本质上是不稳定的,已建立的连接可能突然不可用,或者连接可用但是服务器报错,这就需要重试/重定向功能,这也是RetryAndFollowUpInterceptor拦截器的分层功能。
应用转网络的桥接功能
BridgeInterceptor是个桥梁,这主要是指他会自动处理一些网络层特有的Header信息,例如Host属性,是HTTP1.1必须的,但应用层并不关心这个属性,这就是由BridgeInterceptor自动处理的。
缓存拦截器
在网络请求中使用缓存是非常必要提速手段,OkHttp专门用了CacheInterceptor拦截器来处理这个功能。 缓存的使用注意包括存储、查询和有效性检查,在OkHttp中:
存储,使用client外观模式来设置存储Cache数据的InternalCache实现类,在走请求链获取Response时记录cache。
查询,在存储Cache数据的InternalCache实现类中,根据Request过滤,来查找Cache。 有效性检查,利用工具类CacheStrategy的getCandidate函数,来判断Cache数据的各项指标是否达到条件。
连接拦截器
在RetryAndFollowUpInterceptor入口处,我们已经分析过,在OkHttp中,连接功能由StreamAlloc实现,提供Address地址、Route路由、RealConnection连接、ConnectionPool线程池复用、身份验证、协议、握手、平台、安全等功能。
在ConnectionInterceptor这一层,其实还没有真正连接网络,它的具体功能很简单,就是准备好request请求、streamAllocation连接资源、httpCodec传输工具、connection连接,为最底层的网络连接服务。
自定义网络拦截器
自定义的网络层拦截器相比应用层拦截器,能直接监测到在线网络请求的数据交换过程。 例如,Http有url重定向机制,如果Http返回码为301,就需要根据Header中Location字段的新url,重新发起一次请求,这样的话,总共会有两次请求。
在应用层的拦截器看来,第一次请求并没有返回有效数据,它只会抓到一次请求,也就是第二次的请求。 但是在网络层的拦截器看来,两次都是网络请求,所以它会抓到两次请求。
网络请求功能拦截器
前面所有的拦截器,都是在准备或处理网络连接前后的数据,只有CallServerInterceptor这个拦截器,是真正连接在线服务的。
它使用ConnectionInterceptor提供的HttpCodec传输工具来发出Request,获取Response,然后用ResponseBuilder生成最终的Response,再层层传递给外层的拦截器。